diff --git a/ChangeLog.md b/ChangeLog.md index e2a8c6884..932775048 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -16,7 +16,8 @@ but some sections are changed to compliant this project. * Added obsidian knight as the second boss * Added limitation to inventories * Added limitation to stackable items -* Added four new debug arguments - `--debug-log-time`, `--debug-log-thread`, `--debug-log-trace`, `--debug-filelog-full` +* Added seven new debug arguments - `--debug-log-time`, `--debug-log-thread`, `--debug-log-trace`, `--debug-filelog-full`, `--debug-level`, `--debug-locale`, `--debug-unloc-tracing` +* Added a new argument for disabling hardware acceleration - `--no-hardware-acceleration` * Added a toggle for HUD display * Added a toggle for simplified effect display * Added a new menu for creative mode @@ -36,6 +37,7 @@ but some sections are changed to compliant this project. * Added coloured sheep (#445) * Added ability to dye sheep and beds (#445) * Cow and sheep now graze on grasses +* Added a trigger to auto-enable hardware acceleration ### Changes @@ -51,6 +53,10 @@ but some sections are changed to compliant this project. * Made languages fallback to English * Improved the tile place indicator * Overhauled debugging actions +* Overhauled the farming system +* Changed the languaging setting menu +* Optimized CPU usage +* Reduced food stamina cost ### Removals @@ -62,6 +68,7 @@ but some sections are changed to compliant this project. * Fixed rendering positioning problem of color code styled texts * Fixed lights disappearing when out of screen * Fixed animals and items destroying plants +* Fixed various old world loading crashes ## [2.1.3] diff --git a/src/client/java/minicraft/core/Initializer.java b/src/client/java/minicraft/core/Initializer.java index 8b6281d10..95872d3b2 100644 --- a/src/client/java/minicraft/core/Initializer.java +++ b/src/client/java/minicraft/core/Initializer.java @@ -36,6 +36,7 @@ static void parseArgs(String[] args) { // Parses command line arguments @Nullable String saveDir = null; + boolean enableHardwareAcceleration = true; for (int i = 0; i < args.length; i++) { if (args[i].equalsIgnoreCase("--savedir") && i + 1 < args.length) { i++; @@ -56,9 +57,13 @@ static void parseArgs(String[] args) { Localization.isDebugLocaleEnabled = true; } else if (args[i].equalsIgnoreCase("--debug-unloc-tracing")) { Localization.unlocalizedStringTracing = true; + } else if (args[i].equalsIgnoreCase("--no-hardware-acceleration")) { + enableHardwareAcceleration = false; } } ((TinylogLoggingProvider) ProviderRegistry.getLoggingProvider()).init(); + // Reference: https://stackoverflow.com/a/13832805 + if (enableHardwareAcceleration) System.setProperty("sun.java2d.opengl", "true"); FileHandler.determineGameDir(saveDir); } diff --git a/src/client/java/minicraft/core/Renderer.java b/src/client/java/minicraft/core/Renderer.java index 01e024f4e..3d2fcce91 100644 --- a/src/client/java/minicraft/core/Renderer.java +++ b/src/client/java/minicraft/core/Renderer.java @@ -23,6 +23,7 @@ import minicraft.item.PotionType; import minicraft.item.ToolItem; import minicraft.item.ToolType; +import minicraft.item.WateringCanItem; import minicraft.level.Level; import minicraft.screen.LoadingDisplay; import minicraft.screen.Menu; @@ -324,6 +325,15 @@ private static void renderGui() { Font.drawBackground(dura + "%", screen, 164, Screen.h - 16, Color.get(1, 255 - green, green, 0)); } + // WATERING CAN CONTAINER STATUS + if (player.activeItem instanceof WateringCanItem) { + // Draws the text + WateringCanItem tin = (WateringCanItem) player.activeItem; + int dura = tin.content * 100 / tin.CAPACITY; + int green = (int)(dura * 2.55f); // Let duration show as normal. + Font.drawBackground(dura + "%", screen, 164, Screen.h - 16, Color.get(1, 255 - green, green, 0)); + } + // This renders the potions overlay if (player.showpotioneffects && player.potioneffects.size() > 0) { diff --git a/src/client/java/minicraft/core/Updater.java b/src/client/java/minicraft/core/Updater.java index 391d94314..fb45c9e29 100644 --- a/src/client/java/minicraft/core/Updater.java +++ b/src/client/java/minicraft/core/Updater.java @@ -126,7 +126,10 @@ public static void tick() { // assert curDisplay == prevDisplay; currentDisplay = displayQuery.peek(); assert currentDisplay != null; - currentDisplay.init(prevDisplay); + if (prevDisplay.getParent() == currentDisplay) + prevDisplay.onExit(); + else + currentDisplay.init(prevDisplay); } Level level = levels[currentLevel]; diff --git a/src/client/java/minicraft/entity/furniture/Chest.java b/src/client/java/minicraft/entity/furniture/Chest.java index b2f186cc6..71944b462 100644 --- a/src/client/java/minicraft/entity/furniture/Chest.java +++ b/src/client/java/minicraft/entity/furniture/Chest.java @@ -77,7 +77,7 @@ public Inventory getInventory() { public void die() { if (level != null) { List items = inventory.getItems(); - level.dropItem(x, y, items.toArray(new Item[items.size()])); + level.dropItem(x, y, items.toArray(new Item[0])); } super.die(); } diff --git a/src/client/java/minicraft/entity/furniture/Composter.java b/src/client/java/minicraft/entity/furniture/Composter.java new file mode 100644 index 000000000..079d90795 --- /dev/null +++ b/src/client/java/minicraft/entity/furniture/Composter.java @@ -0,0 +1,77 @@ +package minicraft.entity.furniture; + +import minicraft.entity.Direction; +import minicraft.entity.mob.Player; +import minicraft.gfx.SpriteLinker; +import minicraft.item.Item; +import minicraft.item.Items; +import minicraft.item.StackableItem; +import org.jetbrains.annotations.Nullable; + +public class Composter extends Furniture { + private static final SpriteLinker.LinkedSprite sprite = new SpriteLinker.LinkedSprite(SpriteLinker.SpriteType.Entity, "composter"); + private static final SpriteLinker.LinkedSprite spriteFilled = new SpriteLinker.LinkedSprite(SpriteLinker.SpriteType.Entity, "composter_filled"); + private static final SpriteLinker.LinkedSprite spriteFull = new SpriteLinker.LinkedSprite(SpriteLinker.SpriteType.Entity, "composter_full"); + private static final SpriteLinker.LinkedSprite itemSprite = new SpriteLinker.LinkedSprite(SpriteLinker.SpriteType.Item, "composter"); + + private static final int MAX_COMPOST = 7; + private int compost = 0; + + public Composter() { + super("Composter", sprite, itemSprite); + } + + @Override + public boolean interact(Player player, @Nullable Item item, Direction attackDir) { + if (compost == MAX_COMPOST) { + compost = 0; + StackableItem i = (StackableItem) Items.get("Fertilizer").copy(); + i.count = 1; + player.getLevel().dropItem(x, y, i); + refreshStatus(); + return true; + } + + if (item instanceof StackableItem) { + // 100% compost as they are heavy food + if (item.getName().equalsIgnoreCase("Baked Potato") || item.getName().equalsIgnoreCase("Bread")) { + compost++; + ((StackableItem) item).count--; + refreshStatus(); + return true; + + // 75% compost as they are crop food + } else if (item.getName().equalsIgnoreCase("Wheat") || item.getName().equalsIgnoreCase("Rose") || + item.getName().equalsIgnoreCase("Flower") || item.getName().equalsIgnoreCase("Apple") || + item.getName().equalsIgnoreCase("Potato") || item.getName().equalsIgnoreCase("Carrot")) { + if (random.nextInt(4) != 0) { // 75% + compost++; + ((StackableItem) item).count--; + refreshStatus(); + return true; + } + + // 66.7& compost as they are seeds + } else if (item.getName().equalsIgnoreCase("Acorn") || item.getName().equalsIgnoreCase("Cactus") || + item.getName().equalsIgnoreCase("Wheat Seeds") || item.getName().equalsIgnoreCase("Grass Seeds")) { + if (random.nextInt(3) != 0) { // 66.7% + compost++; + ((StackableItem) item).count--; + refreshStatus(); + return true; + } + } + } + + return false; + } + + private void refreshStatus() { + if (compost == 0) + super.sprite = sprite; + else if (compost < MAX_COMPOST) + super.sprite = spriteFilled; + else + super.sprite = spriteFull; + } +} diff --git a/src/client/java/minicraft/entity/mob/Cow.java b/src/client/java/minicraft/entity/mob/Cow.java index f7acb4078..a3baf9f4b 100644 --- a/src/client/java/minicraft/entity/mob/Cow.java +++ b/src/client/java/minicraft/entity/mob/Cow.java @@ -31,12 +31,9 @@ public void die() { @Override public void tick() { super.tick(); - if (random.nextInt(1000) == 0) { // Grazing without any benefits. - Tile tile = level.getTile(x >> 4, y >> 4); - // If tall grasses are present, these are consumed and then turn into grass tiles. - if (tile instanceof GrassTile) { - level.setTile(x >> 4, y >> 4, Tiles.get("dirt")); - } + Tile tile = level.getTile(x >> 4, y >> 4); + if (tile instanceof GrassTile && random.nextInt(1000) == 0) { // Grazing without any benefits. + level.setTile(x >> 4, y >> 4, Tiles.get("dirt")); } } } diff --git a/src/client/java/minicraft/entity/mob/Mob.java b/src/client/java/minicraft/entity/mob/Mob.java index f0bc64aeb..5484c6c15 100644 --- a/src/client/java/minicraft/entity/mob/Mob.java +++ b/src/client/java/minicraft/entity/mob/Mob.java @@ -129,12 +129,12 @@ private boolean move(int xd, int yd, boolean changeDir) { // Knockback shouldn't /** The mob immediately despawns if the distance of the closest player is greater than the return value of this. */ protected int getDespawnDistance() { - return 80; + return 1280; } /** The mob randomly despawns if the distance of the closest player is greater than the return value of this. */ protected int getNoDespawnDistance() { - return 40; + return 640; } /** @see #handleDespawn() */ diff --git a/src/client/java/minicraft/entity/mob/MobAi.java b/src/client/java/minicraft/entity/mob/MobAi.java index 8195e9171..61df74259 100644 --- a/src/client/java/minicraft/entity/mob/MobAi.java +++ b/src/client/java/minicraft/entity/mob/MobAi.java @@ -66,7 +66,10 @@ public void handleDespawn() { */ protected boolean isWithinLight() { return Arrays.stream(level.getEntityArray()).anyMatch(e -> e instanceof Lantern && isWithin(e.getLightRadius(), e)) - || !level.getMatchingTiles((tile, x, y) -> Math.hypot(Math.abs(this.x - x), Math.abs(this.y - y)) <= tile.getLightRadius(level, x, y)).isEmpty(); + || !level.getMatchingTiles((tile, x, y) -> { + int xx = Math.abs(this.x - x), yy = Math.abs(this.y - y), l = tile.getLightRadius(level, x, y); + return xx * xx + yy * yy <= l * l; + }).isEmpty(); } /** @@ -84,7 +87,7 @@ public void tick() { if (lifetime > 0) { age++; if (age > lifetime) { - boolean playerClose = getLevel().entityNearPlayer((Entity) this); + boolean playerClose = getLevel().entityNearPlayer(this); if (!playerClose) { remove(); diff --git a/src/client/java/minicraft/entity/mob/ObsidianKnight.java b/src/client/java/minicraft/entity/mob/ObsidianKnight.java index 84f833338..8ee4bcf89 100644 --- a/src/client/java/minicraft/entity/mob/ObsidianKnight.java +++ b/src/client/java/minicraft/entity/mob/ObsidianKnight.java @@ -16,6 +16,7 @@ import minicraft.gfx.SpriteLinker; import minicraft.item.Items; import minicraft.screen.AchievementsDisplay; +import org.jetbrains.annotations.Range; public class ObsidianKnight extends EnemyMob { private static final SpriteLinker.LinkedSprite[][][] armored = new SpriteLinker.LinkedSprite[][][] { @@ -36,8 +37,9 @@ public class ObsidianKnight extends EnemyMob { public static boolean beaten = false; // If the boss was beaten public static boolean active = false; // If the boss is active - private static int phase = 0; // The phase of the boss. {0, 1} - private static int attackPhaseCooldown = 0; // Cooldown between attacks + @Range(from = 0, to = 1) + private int phase = 0; // The phase of the boss. {0, 1} + private int attackPhaseCooldown = 0; // Cooldown between attacks private AttackPhase attackPhase = AttackPhase.Attacking; private enum AttackPhase { Attacking, Dashing, Walking; } // Using fire sparks in attacking. @@ -77,12 +79,10 @@ public void tick() { this.remove(); } - //Achieve phase2 - if (health <= 2500) { + // Achieve phase 2 + if (health <= 2500 && phase == 0) { // Assume that phase would not turn back to phase 1 phase = 1; - } - if (phase == 1) { - lvlSprites = broken; + lvlSprites = broken; // Refreshing phased sprites } if (Game.isMode("minicraft.settings.mode.creative")) return; // Should not attack if player is in creative @@ -134,7 +134,7 @@ public void tick() { else if (atan2 < -67.5) attackDir = -90; else if (atan2 < -22.5) attackDir = -45; else attackDir = 0; - double speed = 1 + attackLevel * 0.2 + attackTime / 10 * 0.01; // speed is dependent on the attackType. (higher attackType, faster speeds) + double speed = 1 + attackLevel * 0.2 + attackTime / 10D * 0.01; // speed is dependent on the attackType. (higher attackType, faster speeds) // The range of attack is 90 degrees. With little random factor. int phi = attackDir - 36 + (attackTime % 5) * 18 + random.nextInt(7) - 3; level.add(new FireSpark(this, Math.cos(Math.toRadians(phi)) * speed, Math.sin(Math.toRadians(phi)) * speed)); // Adds a spark entity with the cosine and sine of dir times speed. diff --git a/src/client/java/minicraft/entity/mob/Player.java b/src/client/java/minicraft/entity/mob/Player.java index 70605fbd0..a21c083f5 100644 --- a/src/client/java/minicraft/entity/mob/Player.java +++ b/src/client/java/minicraft/entity/mob/Player.java @@ -639,7 +639,11 @@ protected void attack() { } // If the interaction between you and an entity is successful, then return. - if (interact(getInteractionBox(INTERACT_DIST))) return; + if (interact(getInteractionBox(INTERACT_DIST))) { + if (activeItem.isDepleted()) + activeItem = null; + return; + } // Attempt to interact with the tile. Point t = getInteractionTile(); diff --git a/src/client/java/minicraft/entity/mob/Sheep.java b/src/client/java/minicraft/entity/mob/Sheep.java index 90a594d49..5aefa8709 100644 --- a/src/client/java/minicraft/entity/mob/Sheep.java +++ b/src/client/java/minicraft/entity/mob/Sheep.java @@ -45,12 +45,9 @@ public void render(Screen screen) { public void tick() { super.tick(); Tile tile = level.getTile(x >> 4, y >> 4); - // If tall grasses are present, these are consumed and then turn into grass tiles. - if (tile instanceof GrassTile) { - if (random.nextInt(1000) == 0) { // Grazing - level.setTile(x >> 4, y >> 4, Tiles.get("dirt")); - cut = false; - } + if (tile instanceof GrassTile && random.nextInt(1000) == 0) { // Grazing + level.setTile(x >> 4, y >> 4, Tiles.get("dirt")); + cut = false; } } diff --git a/src/client/java/minicraft/entity/particle/WaterParticle.java b/src/client/java/minicraft/entity/particle/WaterParticle.java new file mode 100644 index 000000000..b91491dc2 --- /dev/null +++ b/src/client/java/minicraft/entity/particle/WaterParticle.java @@ -0,0 +1,58 @@ +package minicraft.entity.particle; + +import minicraft.gfx.SpriteLinker; + +public class WaterParticle extends Particle { + private final int destX; + private final int destY; + private int count; + private boolean stopped; + + public WaterParticle(int x, int y, int lifetime, SpriteLinker.LinkedSprite sprite, int destX, int destY) { + super(x, y, lifetime, sprite); + this.destX = destX; + this.destY = destY; + count = 0; + stopped = false; + } + + @Override + public void tick() { + move: + if (!stopped) { + count++; + if (x == destX && y == destY) { + stopped = true; + break move; + } + if (count == 2) { + int diffX = destX - x; + int diffY = destY - y; + if (Math.abs(diffX) < 3 && Math.abs(diffY) < 3) { + move(destX, destY); + stopped = true; + break move; + } + + double phi = Math.atan2(diffY, diffX); + double moveX = Math.cos(phi); + double moveY = Math.sin(phi); + int moveXI = 0; + int moveYI = 0; + if (Math.abs(moveX / moveY) > 1.4) moveXI = (int) Math.signum(moveX); // Difference in X is greater. + else if (Math.abs(moveY / moveX) > 1.4) + moveYI = (int) Math.signum(moveY); // Difference in Y is greater. + else { // The difference is small. + moveXI = (int) Math.signum(moveX); + moveYI = (int) Math.signum(moveY); + } + + if (!move(moveXI, moveYI)) + stopped = true; + count = 0; + } + } + + super.tick(); + } +} diff --git a/src/client/java/minicraft/item/FurnitureItem.java b/src/client/java/minicraft/item/FurnitureItem.java index 0bf800a8d..b65ad2920 100644 --- a/src/client/java/minicraft/item/FurnitureItem.java +++ b/src/client/java/minicraft/item/FurnitureItem.java @@ -5,6 +5,7 @@ import minicraft.entity.Direction; import minicraft.entity.furniture.Bed; import minicraft.entity.furniture.Chest; +import minicraft.entity.furniture.Composter; import minicraft.entity.furniture.Crafter; import minicraft.entity.furniture.DungeonChest; import minicraft.entity.furniture.Furniture; @@ -57,6 +58,7 @@ protected static ArrayList getAllInstances() { items.add(new FurnitureItem(new Tnt())); items.add(new FurnitureItem(new Bed())); + items.add(new FurnitureItem(new Composter())); return items; } diff --git a/src/client/java/minicraft/item/Items.java b/src/client/java/minicraft/item/Items.java index 9430bd63c..ce9bbee5b 100644 --- a/src/client/java/minicraft/item/Items.java +++ b/src/client/java/minicraft/item/Items.java @@ -18,7 +18,7 @@ This class is meant to define all the different kinds of items in minicraft. Ite If you want to access one of those items, you do it through this class, by calling get("item name"); casing does not matter. */ - private static ArrayList items = new ArrayList<>(); + private static final ArrayList items = new ArrayList<>(); private static void add(Item i) { items.add(i); @@ -43,6 +43,7 @@ private static void addAll(ArrayList items) { addAll(FishingRodItem.getAllInstances()); addAll(SummonItem.getAllInstances()); addAll(HeartItem.getAllInstances()); + addAll(WateringCanItem.getAllInstances()); } public static ArrayList getAll() { @@ -106,6 +107,8 @@ public static Item get(String name, boolean allowNull) { ((StackableItem)i).count = data; if (i instanceof ToolItem && hadUnderscore) ((ToolItem)i).dur = data; + if (i instanceof WateringCanItem) + ((WateringCanItem) i).content = data; return i; } else { Logging.ITEMS.error("Requested invalid item with name: '{}'", name); diff --git a/src/client/java/minicraft/item/Recipes.java b/src/client/java/minicraft/item/Recipes.java index 80daca00a..2ad9218c7 100644 --- a/src/client/java/minicraft/item/Recipes.java +++ b/src/client/java/minicraft/item/Recipes.java @@ -113,6 +113,7 @@ public class Recipes { anvilRecipes.add(new Recipe("Gem Shovel_1", "Wood_5", "gem_50")); anvilRecipes.add(new Recipe("Gem Bow_1", "Wood_5", "gem_50", "string_2")); anvilRecipes.add(new Recipe("Shears_1", "Iron_4")); + anvilRecipes.add(new Recipe("Watering Can_1", "Iron_3")); furnaceRecipes.add(new Recipe("iron_1", "iron Ore_4", "coal_1")); furnaceRecipes.add(new Recipe("gold_1", "gold Ore_4", "coal_1")); @@ -138,5 +139,6 @@ public class Recipes { enchantRecipes.add(new Recipe("Escape Potion_1", "awkward potion_1", "GunPowder_3", "Lapis_7")); enchantRecipes.add(new Recipe("Totem of Air_1", "gold_10", "gem_10", "Lapis_5","Cloud Ore_5")); enchantRecipes.add(new Recipe("Obsidian Poppet_1", "gold_10", "gem_10", "Lapis_5","Shard_15")); + enchantRecipes.add(new Recipe("Arcane Fertilizer_3", "Lapis_6", "Bone_2")); } } diff --git a/src/client/java/minicraft/item/StackableItem.java b/src/client/java/minicraft/item/StackableItem.java index 9275a29c5..73b0634e5 100644 --- a/src/client/java/minicraft/item/StackableItem.java +++ b/src/client/java/minicraft/item/StackableItem.java @@ -2,6 +2,7 @@ import minicraft.core.Game; import minicraft.core.io.Localization; +import minicraft.gfx.SpriteLinker; import minicraft.gfx.SpriteLinker.LinkedSprite; import minicraft.gfx.SpriteLinker.SpriteType; import org.jetbrains.annotations.NotNull; @@ -37,6 +38,9 @@ protected static ArrayList getAllInstances() { items.add(new StackableItem("Shard", new LinkedSprite(SpriteType.Item, "shard"))); items.add(new StackableItem("Cloud Ore", new LinkedSprite(SpriteType.Item, "cloud_ore"))); items.add(new StackableItem("Glass Bottle", new LinkedSprite(SpriteType.Item, "glass_bottle"))); + items.add(new StackableItem("Tomato", new LinkedSprite(SpriteType.Item, "tomato"))); + items.add(new StackableItem("Bone", new LinkedSprite(SpriteType.Item, "bone"))); + items.add(new StackableItem("Fertilizer", new SpriteLinker.LinkedSprite(SpriteLinker.SpriteType.Item, "fertilizer"))); return items; } diff --git a/src/client/java/minicraft/item/SummonItem.java b/src/client/java/minicraft/item/SummonItem.java index de4508c5d..15c0d286e 100644 --- a/src/client/java/minicraft/item/SummonItem.java +++ b/src/client/java/minicraft/item/SummonItem.java @@ -3,6 +3,7 @@ import minicraft.core.Game; import minicraft.core.io.Localization; import minicraft.entity.Direction; +import minicraft.entity.Entity; import minicraft.entity.furniture.KnightStatue; import minicraft.entity.mob.AirWizard; import minicraft.entity.mob.ObsidianKnight; @@ -68,12 +69,23 @@ public boolean interactOn(Tile tile, Level level, int xt, int yt, Player player, // If the player nears the center. if (new Rectangle(level.w/2-3, level.h/2-3, 7, 7).contains(player.x >> 4, player.y >> 4)) { if (!ObsidianKnight.active) { + boolean exists = false; + for (Entity e : level.getEntityArray()) { + if (e instanceof KnightStatue) { + exists = true; + break; + } + } - // Pay stamina - if (player.payStamina(2)) { - level.add(new KnightStatue(5000), level.w/2, level.h/2, true); - Logger.tag("SummonItem").debug("Summoned new Knight Statue"); - success = true; + if (!exists) { // Prevent unintended behaviors + // Pay stamina + if (player.payStamina(2)) { + level.add(new KnightStatue(5000), level.w/2, level.h/2, true); + Logger.tag("SummonItem").debug("Summoned new Knight Statue"); + success = true; + } + } else { + Game.notifications.add(Localization.getLocalized("minicraft.notification.knight_statue_exists")); } } else { Game.notifications.add(Localization.getLocalized("minicraft.notification.boss_limit")); diff --git a/src/client/java/minicraft/item/TileItem.java b/src/client/java/minicraft/item/TileItem.java index a645dcef0..ffa1a91d7 100644 --- a/src/client/java/minicraft/item/TileItem.java +++ b/src/client/java/minicraft/item/TileItem.java @@ -11,8 +11,10 @@ import minicraft.level.Level; import minicraft.level.tile.Tile; import minicraft.level.tile.Tiles; +import minicraft.screen.AchievementsDisplay; import minicraft.util.AdvancementElement; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.tinylog.Logger; import java.util.ArrayList; @@ -25,102 +27,138 @@ protected static ArrayList getAllInstances() { ArrayList items = new ArrayList<>(); /// TileItem sprites all have 1x1 sprites. - items.add(new TileItem("Flower", new LinkedSprite(SpriteType.Item, "white_flower"), "flower", "grass")); - items.add(new TileItem("Acorn", new LinkedSprite(SpriteType.Item, "acorn"), "tree Sapling", "grass")); - items.add(new TileItem("Dirt", new LinkedSprite(SpriteType.Item, "dirt"), "dirt", "hole", "water", "lava")); - items.add(new TileItem("Natural Rock", new LinkedSprite(SpriteType.Item, "stone"), "rock", "hole", "dirt", "sand", "grass", "path", "water", "lava")); - - items.add(new TileItem("Plank", new LinkedSprite(SpriteType.Item, "plank"), "Wood Planks", "hole", "water", "cloud")); - items.add(new TileItem("Plank Wall", new LinkedSprite(SpriteType.Item, "plank_wall"), "Wood Wall", "Wood Planks")); - items.add(new TileItem("Wood Door", new LinkedSprite(SpriteType.Item, "wood_door"), "Wood Door", "Wood Planks")); - items.add(new TileItem("Wood Fence", new LinkedSprite(SpriteType.Item, "wood_fence"), "Wood Fence", "grass")); - items.add(new TileItem("Stone", new LinkedSprite(SpriteType.Item, "stone"), "Stone", "hole", "water", "cloud", "lava")); - items.add(new TileItem("Stone Brick", new LinkedSprite(SpriteType.Item, "stone_brick"), "Stone Bricks", "hole", "water", "cloud", "lava")); - items.add(new TileItem("Ornate Stone", new LinkedSprite(SpriteType.Item, "stone_brick"), "Ornate Stone", "hole", "water", "cloud", "lava")); - items.add(new TileItem("Stone Wall", new LinkedSprite(SpriteType.Item, "stone_wall"), "Stone Wall", "Stone Bricks")); - items.add(new TileItem("Stone Door", new LinkedSprite(SpriteType.Item, "stone_wall"), "Stone Door", "Stone Bricks")); - items.add(new TileItem("Stone Fence", new LinkedSprite(SpriteType.Item, "stone_fence"), "Stone Fence", "Stone Bricks")); - items.add(new TileItem("Raw Obsidian", new LinkedSprite(SpriteType.Item, "obsidian"), "Raw Obsidian", "hole", "water", "cloud", "lava")); - items.add(new TileItem("Obsidian Brick", new LinkedSprite(SpriteType.Item, "obsidian_brick"), "Obsidian", "hole", "water", "cloud", "lava")); - items.add(new TileItem("Ornate Obsidian", new LinkedSprite(SpriteType.Item, "obsidian_brick"), "Ornate Obsidian","hole", "water", "cloud", "lava")); - items.add(new TileItem("Obsidian Wall", new LinkedSprite(SpriteType.Item, "obsidian_wall"), "Obsidian Wall", "Obsidian")); - items.add(new TileItem("Obsidian Door", new LinkedSprite(SpriteType.Item, "obsidian_door"), "Obsidian Door", "Obsidian")); - items.add(new TileItem("Obsidian Fence", new LinkedSprite(SpriteType.Item, "obsidian_fence"), "Obsidian Fence", "Obsidian")); - - items.add(new TileItem("Wool", new LinkedSprite(SpriteType.Item, "wool"), "Wool", "hole", "water")); - items.add(new TileItem("Red Wool", new LinkedSprite(SpriteType.Item, "red_wool"), "Red Wool", "hole", "water")); - items.add(new TileItem("Blue Wool", new LinkedSprite(SpriteType.Item, "blue_wool"), "Blue Wool", "hole", "water")); - items.add(new TileItem("Green Wool", new LinkedSprite(SpriteType.Item, "green_wool"), "Green Wool", "hole", "water")); - items.add(new TileItem("Yellow Wool", new LinkedSprite(SpriteType.Item, "yellow_wool"), "Yellow Wool", "hole", "water")); - items.add(new TileItem("Black Wool", new LinkedSprite(SpriteType.Item, "black_wool"), "Black Wool", "hole", "water")); - - items.add(new TileItem("Sand", new LinkedSprite(SpriteType.Item, "sand"), "sand", "hole", "water", "lava")); - items.add(new TileItem("Cactus", new LinkedSprite(SpriteType.Item, "cactus"), "cactus Sapling", "sand")); - items.add(new TileItem("Bone", new LinkedSprite(SpriteType.Item, "bone"), "tree", "tree Sapling")); - items.add(new TileItem("Cloud", new LinkedSprite(SpriteType.Item, "cloud"), "cloud", "Infinite Fall")); - - items.add(new TileItem("Wheat Seeds", new LinkedSprite(SpriteType.Item, "seed"), "wheat", "farmland")); - items.add(new TileItem("Potato", new LinkedSprite(SpriteType.Item, "potato"), "potato", "farmland")); - items.add(new TileItem("Grass Seeds", new LinkedSprite(SpriteType.Item, "seed"), "grass", "dirt")); + items.add(new TileItem("Flower", new LinkedSprite(SpriteType.Item, "white_flower"), new TileModel("flower"), "grass")); + items.add(new TileItem("Acorn", new LinkedSprite(SpriteType.Item, "acorn"), new TileModel("tree Sapling"), "grass")); + items.add(new TileItem("Dirt", new LinkedSprite(SpriteType.Item, "dirt"), new TileModel("dirt"), "hole", "water", "lava")); + items.add(new TileItem("Natural Rock", new LinkedSprite(SpriteType.Item, "stone"), new TileModel("rock"), "hole", "dirt", "sand", "grass", "path", "water", "lava")); + + items.add(new TileItem("Plank", new LinkedSprite(SpriteType.Item, "plank"), new TileModel("Wood Planks"), "hole", "water", "cloud")); + items.add(new TileItem("Plank Wall", new LinkedSprite(SpriteType.Item, "plank_wall"), new TileModel("Wood Wall"), "Wood Planks")); + items.add(new TileItem("Wood Door", new LinkedSprite(SpriteType.Item, "wood_door"), new TileModel("Wood Door"), "Wood Planks")); + items.add(new TileItem("Wood Fence", new LinkedSprite(SpriteType.Item, "wood_fence"), new TileModel("Wood Fence"), "grass")); + items.add(new TileItem("Stone", new LinkedSprite(SpriteType.Item, "stone"), new TileModel("Stone"), "hole", "water", "cloud", "lava")); + items.add(new TileItem("Stone Brick", new LinkedSprite(SpriteType.Item, "stone_brick"), new TileModel("Stone Bricks"), "hole", "water", "cloud", "lava")); + items.add(new TileItem("Ornate Stone", new LinkedSprite(SpriteType.Item, "stone_brick"), new TileModel("Ornate Stone"), "hole", "water", "cloud", "lava")); + items.add(new TileItem("Stone Wall", new LinkedSprite(SpriteType.Item, "stone_wall"), new TileModel("Stone Wall"), "Stone Bricks")); + items.add(new TileItem("Stone Door", new LinkedSprite(SpriteType.Item, "stone_wall"), new TileModel("Stone Door"), "Stone Bricks")); + items.add(new TileItem("Stone Fence", new LinkedSprite(SpriteType.Item, "stone_fence"), new TileModel("Stone Fence"), "Stone Bricks")); + items.add(new TileItem("Raw Obsidian", new LinkedSprite(SpriteType.Item, "obsidian"), new TileModel("Raw Obsidian"), "hole", "water", "cloud", "lava")); + items.add(new TileItem("Obsidian Brick", new LinkedSprite(SpriteType.Item, "obsidian_brick"), new TileModel("Obsidian"), "hole", "water", "cloud", "lava")); + items.add(new TileItem("Ornate Obsidian", new LinkedSprite(SpriteType.Item, "obsidian_brick"), new TileModel("Ornate Obsidian"),"hole", "water", "cloud", "lava")); + items.add(new TileItem("Obsidian Wall", new LinkedSprite(SpriteType.Item, "obsidian_wall"), new TileModel("Obsidian Wall"), "Obsidian")); + items.add(new TileItem("Obsidian Door", new LinkedSprite(SpriteType.Item, "obsidian_door"), new TileModel("Obsidian Door"), "Obsidian")); + items.add(new TileItem("Obsidian Fence", new LinkedSprite(SpriteType.Item, "obsidian_fence"), new TileModel("Obsidian Fence"), "Obsidian")); + + items.add(new TileItem("Wool", new LinkedSprite(SpriteType.Item, "wool"), new TileModel("Wool"), "hole", "water")); + items.add(new TileItem("Red Wool", new LinkedSprite(SpriteType.Item, "red_wool"), new TileModel("Red Wool"), "hole", "water")); + items.add(new TileItem("Blue Wool", new LinkedSprite(SpriteType.Item, "blue_wool"), new TileModel("Blue Wool"), "hole", "water")); + items.add(new TileItem("Green Wool", new LinkedSprite(SpriteType.Item, "green_wool"), new TileModel("Green Wool"), "hole", "water")); + items.add(new TileItem("Yellow Wool", new LinkedSprite(SpriteType.Item, "yellow_wool"), new TileModel("Yellow Wool"), "hole", "water")); + items.add(new TileItem("Black Wool", new LinkedSprite(SpriteType.Item, "black_wool"), new TileModel("Black Wool"), "hole", "water")); + + items.add(new TileItem("Sand", new LinkedSprite(SpriteType.Item, "sand"), new TileModel("sand"), "hole", "water", "lava")); + items.add(new TileItem("Cactus", new LinkedSprite(SpriteType.Item, "cactus"), new TileModel("cactus Sapling"), "sand")); + items.add(new TileItem("Cloud", new LinkedSprite(SpriteType.Item, "cloud"), new TileModel("cloud"), "Infinite Fall")); + + TileModel.TileDataGetter seedPlanting = (model1, target, level, xt, yt, player, attackDir) -> { + AchievementsDisplay.setAchievement("minicraft.achievement.plant_seed", true); + return TileModel.KEEP_DATA.getTileData(model1, target, level, xt, yt, player, attackDir); + }; + items.add(new TileItem("Wheat Seeds", new LinkedSprite(SpriteType.Item, "seed"), new TileModel("wheat", seedPlanting), "farmland")); + items.add(new TileItem("Potato", new LinkedSprite(SpriteType.Item, "potato"), new TileModel("potato", TileModel.KEEP_DATA), "farmland")); + items.add(new TileItem("Carrot", new LinkedSprite(SpriteType.Item, "carrot"), new TileModel("carrot", TileModel.KEEP_DATA), "farmland")); + items.add(new TileItem("Tomato Seeds", new LinkedSprite(SpriteType.Item, "seed"), new TileModel("tomato", seedPlanting), "farmland")); + items.add(new TileItem("Heavenly Berries", new LinkedSprite(SpriteType.Item, "heavenly_berries"), new TileModel("heavenly berries", TileModel.KEEP_DATA), "farmland")); + items.add(new TileItem("Hellish Berries", new LinkedSprite(SpriteType.Item, "hellish_berries"), new TileModel("hellish berries", TileModel.KEEP_DATA), "farmland")); + items.add(new TileItem("Grass Seeds", new LinkedSprite(SpriteType.Item, "seed"), new TileModel("grass"), "dirt")); // Creative mode available tiles: - items.add(new TileItem("Farmland", SpriteLinker.missingTexture(SpriteType.Item), "farmland", "dirt", "grass", "hole")); - items.add(new TileItem("Exploded", SpriteLinker.missingTexture(SpriteType.Item), "explode", "dirt", "grass")); - items.add(new TileItem("hole", SpriteLinker.missingTexture(SpriteType.Item), "hole", "dirt", "grass")); - items.add(new TileItem("lava", SpriteLinker.missingTexture(SpriteType.Item), "lava", "dirt", "grass", "hole")); - items.add(new TileItem("path", SpriteLinker.missingTexture(SpriteType.Item), "path", "dirt", "grass", "hole")); - items.add(new TileItem("water", SpriteLinker.missingTexture(SpriteType.Item), "water", "dirt", "grass", "hole")); + items.add(new TileItem("Farmland", SpriteLinker.missingTexture(SpriteType.Item), new TileModel("farmland"), "dirt", "grass", "hole")); + items.add(new TileItem("hole", SpriteLinker.missingTexture(SpriteType.Item), new TileModel("hole"), "dirt", "grass")); + items.add(new TileItem("lava", SpriteLinker.missingTexture(SpriteType.Item), new TileModel("lava"), "dirt", "grass", "hole")); + items.add(new TileItem("path", SpriteLinker.missingTexture(SpriteType.Item), new TileModel("path"), "dirt", "grass", "hole")); + items.add(new TileItem("water", SpriteLinker.missingTexture(SpriteType.Item), new TileModel("water"), "dirt", "grass", "hole")); return items; } - public final String model; + public final @Nullable TileModel model; public final List validTiles; - protected TileItem(String name, LinkedSprite sprite, String model, String... validTiles) { + protected TileItem(String name, LinkedSprite sprite, TileModel model, String... validTiles) { this(name, sprite, 1, model, Arrays.asList(validTiles)); } - protected TileItem(String name, LinkedSprite sprite, int count, String model, String... validTiles) { + protected TileItem(String name, LinkedSprite sprite, int count, TileModel model, String... validTiles) { this(name, sprite, count, model, Arrays.asList(validTiles)); } - protected TileItem(String name, LinkedSprite sprite, int count, String model, List validTiles) { + protected TileItem(String name, LinkedSprite sprite, int count, @Nullable TileModel model, List validTiles) { super(name, sprite, count); - this.model = model.toUpperCase(); + this.model = model; this.validTiles = new ArrayList<>(); for (String tile: validTiles) this.validTiles.add(tile.toUpperCase()); } + public static class TileModel { + public static final TileDataGetter DEFAULT_DATA = ((model, target, level, xt, yt, player, attackDir) -> model.getDefaultData()); + public static final TileDataGetter KEEP_DATA = ((model, target, level, xt, yt, player, attackDir) -> level.getData(xt, yt)); + + public final @NotNull String tile; + public final TileDataGetter data; + + @FunctionalInterface + interface TileDataGetter { + int getTileData(Tile model, Tile target, Level level, int xt, int yt, Player player, Direction attackDir); + } + + public TileModel(String tile) { this(tile, DEFAULT_DATA); } + public TileModel(String tile, TileDataGetter data) { + this.tile = tile.toUpperCase(); + this.data = data; + } + + public static Tile getTile(@Nullable TileModel model) { + return model == null ? Tiles.get(0) : Tiles.get(model.tile); + } + + public static int getTileData(@Nullable TileModel model, Tile tile, Tile target, Level level, int xt, int yt, Player player, Direction attackDir) { + if (model == null) return DEFAULT_DATA.getTileData(tile, target, level, xt, yt, player, attackDir); + return model.data.getTileData(tile, target, level, xt, yt, player, attackDir); + } + } + public boolean interactOn(Tile tile, Level level, int xt, int yt, Player player, Direction attackDir) { for (String tilename : validTiles) { if (tile.matches(level.getData(xt, yt), tilename)) { - level.setTile(xt, yt, model); // TODO maybe data should be part of the saved tile..? + Tile t = TileModel.getTile(model); + level.setTile(xt, yt, t, TileModel.getTileData(model, t, tile, level, xt, yt, player, attackDir)); AdvancementElement.AdvancementTrigger.PlacedTileTrigger.INSTANCE.trigger( new AdvancementElement.AdvancementTrigger.PlacedTileTrigger.PlacedTileTriggerConditionHandler.PlacedTileTriggerConditions( this, level.getTile(xt, yt), level.getData(xt, yt), xt, yt, level.depth )); Sound.play("craft"); - return super.interactOn(true); } } Logger.tag("TileItem").debug("{} cannot be placed on {}.", model, tile.name); - String note = ""; - if (model.contains("WALL")) { - note = Localization.getLocalized("minicraft.notification.invalid_placement", Tiles.getName(validTiles.get(0))); - } - else if (model.contains("DOOR")) { - note = Localization.getLocalized("minicraft.notification.invalid_placement", Tiles.getName(validTiles.get(0))); - } - else if ((model.contains("BRICK") || model.contains("PLANK") || model.equals("STONE") || model.contains("ORNATE"))) { - note = Localization.getLocalized("minicraft.notification.dig_hole"); - } + if (model != null) { + String note = ""; + if (model.tile.contains("WALL")) { + note = Localization.getLocalized("minicraft.notification.invalid_placement", Tiles.getName(validTiles.get(0))); + } + else if (model.tile.contains("DOOR")) { + note = Localization.getLocalized("minicraft.notification.invalid_placement", Tiles.getName(validTiles.get(0))); + } + else if ((model.tile.contains("BRICK") || model.tile.contains("PLANK") || model.tile.equals("STONE") || model.tile.contains("ORNATE"))) { + note = Localization.getLocalized("minicraft.notification.dig_hole"); + } - if (note.length() > 0) { - Game.notifications.add(note); + if (note.length() > 0) { + Game.notifications.add(note); + } } return super.interactOn(false); @@ -128,11 +166,11 @@ else if ((model.contains("BRICK") || model.contains("PLANK") || model.equals("ST @Override public boolean equals(Item other) { - return super.equals(other) && model.equals(((TileItem)other).model); + return super.equals(other) && (model == null || model.equals(((TileItem)other).model)); } @Override - public int hashCode() { return super.hashCode() + model.hashCode(); } + public int hashCode() { return super.hashCode() + (model == null ? 0xFF123 : model.hashCode()); } public @NotNull TileItem copy() { return new TileItem(getName(), sprite, count, model, validTiles); diff --git a/src/client/java/minicraft/item/TorchItem.java b/src/client/java/minicraft/item/TorchItem.java index 6df20168b..2eee762eb 100644 --- a/src/client/java/minicraft/item/TorchItem.java +++ b/src/client/java/minicraft/item/TorchItem.java @@ -21,7 +21,7 @@ public static ArrayList getAllInstances() { private TorchItem() { this(1); } private TorchItem(int count) { - super("Torch", new LinkedSprite(SpriteType.Item, "torch"), count, "", "dirt", "Wood Planks", "Stone Bricks", "Obsidian", "Wool", "Red Wool", "Blue Wool", "Green Wool", "Yellow Wool", "Black Wool", "grass", "sand","path","ornate stone","ornate obsidian"); + super("Torch", new LinkedSprite(SpriteType.Item, "torch"), count, null, "dirt", "Wood Planks", "Stone Bricks", "Obsidian", "Wool", "Red Wool", "Blue Wool", "Green Wool", "Yellow Wool", "Black Wool", "grass", "sand","path","ornate stone","ornate obsidian"); } public boolean interactOn(Tile tile, Level level, int xt, int yt, Player player, Direction attackDir) { diff --git a/src/client/java/minicraft/item/WateringCanItem.java b/src/client/java/minicraft/item/WateringCanItem.java new file mode 100644 index 000000000..7ef7d1dd0 --- /dev/null +++ b/src/client/java/minicraft/item/WateringCanItem.java @@ -0,0 +1,141 @@ +package minicraft.item; + +import minicraft.entity.Direction; +import minicraft.entity.mob.Player; +import minicraft.entity.particle.Particle; +import minicraft.entity.particle.WaterParticle; +import minicraft.gfx.Point; +import minicraft.gfx.SpriteLinker; +import minicraft.level.Level; +import minicraft.level.tile.DirtTile; +import minicraft.level.tile.GrassTile; +import minicraft.level.tile.Tile; +import minicraft.level.tile.Tiles; +import minicraft.level.tile.WaterTile; +import minicraft.level.tile.farming.CropTile; +import org.jetbrains.annotations.NotNull; + +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Map; +import java.util.Random; + +public class WateringCanItem extends Item { + protected static ArrayList getAllInstances() { + ArrayList items = new ArrayList<>(); + items.add(new WateringCanItem("Watering Can")); + return items; + } + + private static final SpriteLinker.LinkedSprite sprite = new SpriteLinker.LinkedSprite(SpriteLinker.SpriteType.Item, "watering_can"); + private static final SpriteLinker.LinkedSprite spriteFilled = new SpriteLinker.LinkedSprite(SpriteLinker.SpriteType.Item, "watering_can_filled"); + private static final SpriteLinker.LinkedSprite particleSprite = new SpriteLinker.LinkedSprite(SpriteLinker.SpriteType.Entity, "glint"); + + private static final SpriteLinker.LinkedSprite[] spriteSplash = new SpriteLinker.LinkedSprite[] { + new SpriteLinker.LinkedSprite(SpriteLinker.SpriteType.Entity, "splash_0"), + new SpriteLinker.LinkedSprite(SpriteLinker.SpriteType.Entity, "splash_1"), + new SpriteLinker.LinkedSprite(SpriteLinker.SpriteType.Entity, "splash_2"), + new SpriteLinker.LinkedSprite(SpriteLinker.SpriteType.Entity, "splash_3") + }; + + public final int CAPACITY = 1800; + public int content = 0; + private int renderingTick = 0; + + protected WateringCanItem(String name) { + super(name, sprite); + } + + @Override + public boolean interactOn(Tile tile, Level level, int xt, int yt, Player player, Direction attackDir) { + if (tile instanceof WaterTile) { + content = CAPACITY; + updateSprite(); + return true; + } else if (content > 0) { + content--; + updateSprite(); + renderingTick++; + Random random = new Random(); + if (renderingTick >= 8) { + for (int i = 0; i < 4; i++) { + SpriteLinker.LinkedSprite splash = spriteSplash[random.nextInt(spriteSplash.length)]; + // 2-pixel deviation for centering particle sprites. + int destX = player.x - 2 + 12 * attackDir.getX() + random.nextInt(9) - 4; + int destY = player.y - 2 + 12 * attackDir.getY() + random.nextInt(9) - 4; + int x = player.x - 2 + 4 * attackDir.getX() + random.nextInt(5) - 2; + int y = player.y - 2 + 4 * attackDir.getY() + random.nextInt(5) - 2; + level.add(new WaterParticle(x, y, 80 + random.nextInt(61) - 30, splash, destX, destY)); + renderingTick = 0; + } + } + if (tile instanceof CropTile) { + int fertilization = ((CropTile) tile).getFertilization(level.getData(xt, yt)); + if (fertilization < 150) { // Maximum of 5 levels watering can can fertilize. + ((CropTile) tile).fertilize(level, xt, yt, 1); + } + if (random.nextInt(5) == 0) { + double x = (double)xt * 16 + 8 + (random.nextGaussian() * 0.5) * 8; + double y = (double)yt * 16 + 8 + (random.nextGaussian() * 0.5) * 8; + level.add(new Particle((int) x, (int) y, 120 + random.nextInt(21) - 40, particleSprite)); + } + } else if (tile instanceof DirtTile || tile instanceof GrassTile) { + if (tile instanceof GrassTile) { + if (random.nextInt(15) == 0) { + double x = (double)xt * 16 + 8 + (random.nextGaussian() * 0.5) * 8; + double y = (double)yt * 16 + 8 + (random.nextGaussian() * 0.5) * 8; + level.add(new Particle((int) x, (int) y, 120 + random.nextInt(21) - 40, particleSprite)); + } + if (random.nextInt(60) == 0) { // Small chance for growing flowers + level.setTile(xt, yt, Tiles.get(2), random.nextInt(2)); + } + } + + for (Point p : level.getAreaTilePositions(xt, yt, 1)) { + Tile t = level.getTile(p.x, p.y); + if (tile instanceof DirtTile) { + if (t instanceof GrassTile) { // Grass tile exists. + if (random.nextInt(5) == 0) { + double x = (double)xt * 16 + 8 + (random.nextGaussian() * 0.5) * 8; + double y = (double)yt * 16 + 8 + (random.nextGaussian() * 0.5) * 8; + level.add(new Particle((int) x, (int) y, 120 + random.nextInt(21) - 40, particleSprite)); + } + if (random.nextInt(10) == 0) + level.setTile(xt, yt, Tiles.get("grass")); // Grass extends. + break; // Operation finished. + } + } else { // tile instanceof GrassTile + if (t instanceof DirtTile) { // Dirt tile exists. + if (random.nextInt(5) == 0) { + double x = (double)xt * 16 + 8 + (random.nextGaussian() * 0.5) * 8; + double y = (double)yt * 16 + 8 + (random.nextGaussian() * 0.5) * 8; + level.add(new Particle((int) x, (int) y, 120 + random.nextInt(21) - 40, particleSprite)); + } + if (random.nextInt(15) == 0) + level.setTile(p.x, p.y, Tiles.get("grass")); // Grass extends. + break; // Operation finished. + } + } + } + } + + return true; + } + + return false; + } + + private void updateSprite() { + super.sprite = content > 0 ? spriteFilled : sprite; + } + + @Override + public String getData() { + return super.getData() + "_" + content; + } + + @Override + public @NotNull Item copy() { + return new WateringCanItem(getName()); + } +} diff --git a/src/client/java/minicraft/level/Level.java b/src/client/java/minicraft/level/Level.java index 57111fbe7..3777ecb74 100644 --- a/src/client/java/minicraft/level/Level.java +++ b/src/client/java/minicraft/level/Level.java @@ -33,6 +33,7 @@ import minicraft.level.tile.Tile; import minicraft.level.tile.Tiles; import minicraft.level.tile.TorchTile; +import minicraft.level.tile.TreeTile; import minicraft.util.Logging; import java.util.ArrayList; @@ -62,6 +63,8 @@ public class Level { public short[] tiles; // An array of all the tiles in the world. public short[] data; // An array of the data of the tiles in the world. + public final TreeTile.TreeType[] treeTypes; // An array of tree types + public final int depth; // Depth level of the level public int monsterDensity = 16; // Affects the number of monsters that are on the level, bigger the number the less monsters spawn. public int maxMobCount; @@ -133,6 +136,37 @@ public Level(int w, int h, long seed, int level, Level parentLevel, boolean make random = new Random(seed); short[][] maps; // Multidimensional array (an array within a array), used for the map + treeTypes = new TreeTile.TreeType[w * h]; + { + LevelGen noise1 = new LevelGen(w, h, 32); + LevelGen noise2 = new LevelGen(w, h, 32); + TreeTile.TreeType[] types = TreeTile.TreeType.values(); + for (int y = 0; y < h; y++) { // Loop through height + for (int x = 0; x < w; x++) { // Loop through width + // Randomly selecting a tree type. + int i = x + y * w; + double val = Math.abs(noise1.values[i] - noise2.values[i]) * 3 - 2; + // This calculates a sort of distance based on the current coordinate. + double xd = x / (w - 1.0) * 2 - 1; + double yd = y / (h - 1.0) * 2 - 1; + if (xd < 0) xd = -xd; + if (yd < 0) yd = -yd; + double dist = Math.max(xd, yd); + dist = dist * dist * dist * dist; + dist = dist * dist * dist * dist; + val += 1 - dist*20; + val += 1.5; // Assuming the range of value is from 0 to 2. + val *= types.length / 2.0; + val += 1; // Incrementing index. + // The original val mainly falls in small interval instead of averagely. + val = 1.0/(3 * types.length) * Math.pow(val - 5, 2); // Quadratically bloating the value. + int idx = (int) Math.round(val - 1); // Decrementing index. + treeTypes[x + y * w] = (idx >= types.length || idx < 0) ? TreeTile.TreeType.OAK // Oak by default. + : types[idx]; + } + } + } + if (level != -4 && level != 0) monsterDensity = 8; @@ -156,6 +190,7 @@ public Level(int w, int h, long seed, int level, Level parentLevel, boolean make tiles = maps[0]; // Assigns the tiles in the map data = maps[1]; // Assigns the data of the tiles + if (level < 0) generateSpawnerStructures(); @@ -534,7 +569,7 @@ public void setTile(int x, int y, Tile t, int dataVal) { public int getData(int x, int y) { if (x < 0 || y < 0 || x >= w || y >= h) return 0; - return data[x + y * w] & 0xff; + return data[x + y * w] & 0xFFFF; } public void setData(int x, int y, int val) { @@ -571,12 +606,13 @@ private void trySpawn() { boolean spawned = false; for (Player player : players) { + assert player.getLevel().depth == depth; int lvl = World.lvlIdx(player.getLevel().depth); for (int i = 0; i < 30 && !spawned; i++) { int rnd = random.nextInt(100); int nx = random.nextInt(w) * 16 + 8, ny = random.nextInt(h) * 16 + 8; double distance = Math.hypot(Math.abs(nx - player.x), Math.abs(ny - player.y)); - if (distance < 10 || distance > 40) continue; // Spawns only between 10 and 40 tiles far from players. + if (distance < 160) continue; // Spawns only far from 10 tiles away. //System.out.println("trySpawn on level " + depth + " of lvl " + lvl + " mob w/ rand " + rnd + " at tile " + nx + "," + ny); diff --git a/src/client/java/minicraft/level/LevelGen.java b/src/client/java/minicraft/level/LevelGen.java index b72c08549..794e5eec9 100644 --- a/src/client/java/minicraft/level/LevelGen.java +++ b/src/client/java/minicraft/level/LevelGen.java @@ -37,7 +37,7 @@ public LevelGen(int w, int h, int featureSize) { } int stepSize = featureSize; - double scale = 2 / w; + double scale = 2.0 / w; double scaleMod = 1; do { int halfStep = stepSize / 2; @@ -48,7 +48,7 @@ public LevelGen(int w, int h, int featureSize) { double c = sample(x, y + stepSize); // Fetches the next value down, possibly looping back to the top of the column. double d = sample(x + stepSize, y + stepSize); // Fetches the value one down, one right. - /** + /* * This could probably use some explaining... Note: the number values are probably only good the first time around... * * This starts with taking the average of the four numbers from before (they form a little square in adjacent tiles), each of which holds a value from -1 to 1. @@ -82,7 +82,7 @@ public LevelGen(int w, int h, int featureSize) { } } - /** + /* * THEN... this stuff is set to repeat the system all over again! * The featureSize is halved, allowing access to further unset mids, and the scale changes... * The scale increases the first time, x1.8, but the second time it's x1.1, and after that probably a little less than 1. So, it generally increases a bit, maybe to 4 / w at tops. This results in the 5th random value being more significant than the first 4 ones in later iterations. @@ -98,7 +98,7 @@ private double sample(int x, int y) { } // This merely returns the value, like Level.getTile(x, y). private void setSample(int x, int y, double value) { - /** + /* * This method is short, but difficult to understand. This is what I think it does: * * The values array is like a 2D array, but formatted into a 1D array; so the basic "x + y * w" is used to access a given value. @@ -114,8 +114,7 @@ private void setSample(int x, int y, double value) { values[(x & (w - 1)) + (y & (h - 1)) * w] = value; } - @Nullable - static short[][] createAndValidateMap(int w, int h, int level, long seed) { + static short[] @Nullable [] createAndValidateMap(int w, int h, int level, long seed) { worldSeed = seed; if (level == 1) @@ -155,7 +154,7 @@ private static short[][] createAndValidateTopMap(int w, int h) { } while (true); } - private static @Nullable short[][] createAndValidateUndergroundMap(int w, int h, int depth) { + private static short[] @Nullable [] createAndValidateUndergroundMap(int w, int h, int depth) { random.setSeed(worldSeed); do { short[][] result = createUndergroundMap(w, h, depth); @@ -196,7 +195,7 @@ private static short[][] createAndValidateDungeon(int w, int h) { } while (true); } - private static @Nullable short[][] createAndValidateSkyMap(int w, int h) { + private static short[] @Nullable [] createAndValidateSkyMap(int w, int h) { random.setSeed(worldSeed); do { @@ -695,7 +694,7 @@ private static short[][] createSkyMap(int w, int h) { double yd = y / (h - 1.0) * 2 - 1; if (xd < 0) xd = -xd; if (yd < 0) yd = -yd; - double dist = xd >= yd ? xd : yd; + double dist = Math.max(xd, yd); dist = dist * dist * dist * dist; dist = dist * dist * dist * dist; val = -val * 1 - 2.2; @@ -777,7 +776,6 @@ public static void main(String[] args) { if (!valid) { maplvls = new int[1]; - maplvls[0] = 0; } //noinspection InfiniteLoopStatement @@ -785,6 +783,7 @@ public static void main(String[] args) { int w = 128; int h = 128; + //noinspection ConstantConditions int lvl = maplvls[idx++ % maplvls.length]; if (lvl > 1 || lvl < -4) continue; @@ -821,11 +820,11 @@ public static void main(String[] args) { } } img.setRGB(0, 0, w, h, pixels, 0, w); - JOptionPane.showMessageDialog(null, null, "Another Map", JOptionPane.PLAIN_MESSAGE, new ImageIcon(img.getScaledInstance(w * 4, h * 4, Image.SCALE_AREA_AVERAGING))); - if (LevelGen.worldSeed == 0x100) - LevelGen.worldSeed = 0xAAFF20; - else - LevelGen.worldSeed = 0x100; + int op = JOptionPane.showOptionDialog(null, null, "Map With Seed "+worldSeed, JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, + new ImageIcon(img.getScaledInstance(w * 4, h * 4, Image.SCALE_AREA_AVERAGING)), new String[] {"Next", "0x100", "0xAAFF20"}, "Next"); + if (op == 1) LevelGen.worldSeed = 0x100; + else if (op == 2) LevelGen.worldSeed = 0xAAFF20; + else LevelGen.worldSeed++; } } } diff --git a/src/client/java/minicraft/level/tile/FlowerTile.java b/src/client/java/minicraft/level/tile/FlowerTile.java index bf09593a0..51be31527 100644 --- a/src/client/java/minicraft/level/tile/FlowerTile.java +++ b/src/client/java/minicraft/level/tile/FlowerTile.java @@ -19,7 +19,7 @@ public class FlowerTile extends Tile { private static final SpriteAnimation flowerSprite1 = new SpriteAnimation(SpriteType.Tile, "flower_shape1"); protected FlowerTile(String name) { - super(name, (SpriteAnimation) null); + super(name, null); connectsToGrass = true; maySpawn = true; } diff --git a/src/client/java/minicraft/level/tile/GrassTile.java b/src/client/java/minicraft/level/tile/GrassTile.java index 5b14bd396..9c916353e 100644 --- a/src/client/java/minicraft/level/tile/GrassTile.java +++ b/src/client/java/minicraft/level/tile/GrassTile.java @@ -13,8 +13,12 @@ import minicraft.level.Level; import minicraft.util.AdvancementElement; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Map; + public class GrassTile extends Tile { - private static SpriteAnimation sprite = new SpriteAnimation(SpriteType.Tile, "grass") + private static final SpriteAnimation sprite = new SpriteAnimation(SpriteType.Tile, "grass") .setConnectChecker((tile, side) -> !side || tile.connectsToGrass) .setSingletonWithConnective(true); @@ -66,7 +70,7 @@ public boolean interact(Level level, int xt, int yt, Player player, Item item, D if (tool.type == ToolType.Hoe) { if (player.payStamina(4 - tool.level) && tool.payDurability()) { int data = level.getData(xt, yt); - level.setTile(xt, yt, Tiles.get("Dirt")); + level.setTile(xt, yt, Tiles.get("Farmland")); Sound.play("monsterhurt"); if (random.nextInt(5) != 0) { // 80% chance to drop Wheat seeds level.dropItem(xt * 16 + 8, yt * 16 + 8, Items.get("Wheat Seeds")); diff --git a/src/client/java/minicraft/level/tile/SaplingTile.java b/src/client/java/minicraft/level/tile/SaplingTile.java index f000acf9e..e3f90849e 100644 --- a/src/client/java/minicraft/level/tile/SaplingTile.java +++ b/src/client/java/minicraft/level/tile/SaplingTile.java @@ -9,7 +9,7 @@ import minicraft.level.Level; public class SaplingTile extends Tile { - private static SpriteAnimation sprite = new SpriteAnimation(SpriteType.Tile, "sapling"); + private static final SpriteAnimation sprite = new SpriteAnimation(SpriteType.Tile, "sapling"); private Tile onType; private Tile growsTo; diff --git a/src/client/java/minicraft/level/tile/Tiles.java b/src/client/java/minicraft/level/tile/Tiles.java index 01e7ee87d..30856124b 100644 --- a/src/client/java/minicraft/level/tile/Tiles.java +++ b/src/client/java/minicraft/level/tile/Tiles.java @@ -1,6 +1,10 @@ package minicraft.level.tile; import minicraft.core.CrashHandler; +import minicraft.level.tile.farming.HeavenlyBerriesTile; +import minicraft.level.tile.farming.HellishBerriesTile; +import minicraft.level.tile.farming.TomatoTile; +import minicraft.level.tile.farming.CarrotTile; import minicraft.level.tile.farming.FarmTile; import minicraft.level.tile.farming.PotatoTile; import minicraft.level.tile.farming.WheatTile; @@ -75,6 +79,10 @@ public static void initTileList() { tiles.put((short)50, new FenceTile(Tile.Material.Wood)); tiles.put((short)51, new FenceTile(Tile.Material.Stone)); tiles.put((short)52, new FenceTile(Tile.Material.Obsidian)); + tiles.put((short)50, new TomatoTile("Tomato")); + tiles.put((short)51, new CarrotTile("Carrot")); + tiles.put((short)52, new HeavenlyBerriesTile("Heavenly Berries")); + tiles.put((short)53, new HellishBerriesTile("Hellish Berries")); // WARNING: don't use this tile for anything! tiles.put((short)255, new ConnectTile()); @@ -86,7 +94,7 @@ public static void initTileList() { } - protected static void add(int id, Tile tile) { + static void add(int id, Tile tile) { tiles.put((short)id, tile); Logging.TILES.debug("Adding " + tile.name + " to tile list with id " + id); tile.id = (short) id; diff --git a/src/client/java/minicraft/level/tile/TreeTile.java b/src/client/java/minicraft/level/tile/TreeTile.java index 895ad1fd4..c4e5c8f3a 100644 --- a/src/client/java/minicraft/level/tile/TreeTile.java +++ b/src/client/java/minicraft/level/tile/TreeTile.java @@ -21,49 +21,87 @@ import minicraft.screen.AchievementsDisplay; import minicraft.util.AdvancementElement; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Random; + public class TreeTile extends Tile { - private static LinkedSprite treeSprite = new LinkedSprite(SpriteType.Tile, "tree"); - private static LinkedSprite treeSpriteFull = new LinkedSprite(SpriteType.Tile, "tree_full"); + private static final LinkedSprite oakSprite = new LinkedSprite(SpriteType.Tile, "oak"); + private static final LinkedSprite oakSpriteFull = new LinkedSprite(SpriteType.Tile, "oak_full"); + private static final LinkedSprite spruceSprite = new LinkedSprite(SpriteType.Tile, "spruce"); + private static final LinkedSprite spruceSpriteFull = new LinkedSprite(SpriteType.Tile, "spruce_full"); + private static final LinkedSprite birchSprite = new LinkedSprite(SpriteType.Tile, "birch"); + private static final LinkedSprite birchSpriteFull = new LinkedSprite(SpriteType.Tile, "birch_full"); + private static final LinkedSprite ashSprite = new LinkedSprite(SpriteType.Tile, "ash"); + private static final LinkedSprite ashSpriteFull = new LinkedSprite(SpriteType.Tile, "ash_full"); + private static final LinkedSprite aspenSprite = new LinkedSprite(SpriteType.Tile, "aspen"); + private static final LinkedSprite aspenSpriteFull = new LinkedSprite(SpriteType.Tile, "aspen_full"); + private static final LinkedSprite firSprite = new LinkedSprite(SpriteType.Tile, "fir"); + private static final LinkedSprite firSpriteFull = new LinkedSprite(SpriteType.Tile, "fir_full"); + private static final LinkedSprite willowSprite = new LinkedSprite(SpriteType.Tile, "willow"); + private static final LinkedSprite willowSpriteFull = new LinkedSprite(SpriteType.Tile, "willow_full"); + + public enum TreeType { + OAK(oakSprite, oakSpriteFull), + SPRUCE(spruceSprite, spruceSpriteFull), + BIRCH(birchSprite, birchSpriteFull), + ASH(ashSprite, ashSpriteFull), + ASPEN(aspenSprite, aspenSpriteFull), + FIR(firSprite, firSpriteFull), + WILLOW(willowSprite, willowSpriteFull); + + private final LinkedSprite treeSprite; + private final LinkedSprite treeSpriteFull; + + TreeType(LinkedSprite treeSprite, LinkedSprite treeSpriteFull) { + this.treeSprite = treeSprite; + this.treeSpriteFull = treeSpriteFull; + } + } protected TreeTile(String name) { super(name, null); connectsToGrass = true; } + @SuppressWarnings("PointlessArithmeticExpression") public void render(Screen screen, Level level, int x, int y) { Tiles.get("Grass").render(screen, level, x, y); - boolean u = level.getTile(x, y - 1) == this; - boolean l = level.getTile(x - 1, y) == this; - boolean r = level.getTile(x + 1, y) == this; - boolean d = level.getTile(x, y + 1) == this; - boolean ul = level.getTile(x - 1, y - 1) == this; - boolean ur = level.getTile(x + 1, y - 1) == this; - boolean dl = level.getTile(x - 1, y + 1) == this; - boolean dr = level.getTile(x + 1, y + 1) == this; - - Sprite sprite = treeSprite.getSprite(); - Sprite spriteFull = treeSpriteFull.getSprite(); - - if (u && ul && l) { - screen.render(x * 16 + 0, y * 16 + 0, spriteFull.spritePixels[0][1]); + TreeType thisType = level.treeTypes[x + y * level.w]; + // Checking whether the target direction has targeted the same TreeTile + boolean isUpTileSame = level.getTile(x, y - 1) == this && thisType == level.treeTypes[x + (y - 1) * level.w]; + boolean isLeftTileSame = level.getTile(x - 1, y) == this && thisType == level.treeTypes[(x - 1) + y * level.w]; + boolean isRightTileSame = level.getTile(x + 1, y) == this && thisType == level.treeTypes[(x + 1) + y * level.w]; + boolean isDownTileSame = level.getTile(x, y + 1) == this && thisType == level.treeTypes[x + (y + 1) * level.w]; + boolean isUpLeftTileSame = level.getTile(x - 1, y - 1) == this && thisType == level.treeTypes[(x - 1) + (y - 1) * level.w]; + boolean isUpRightTileSame = level.getTile(x + 1, y - 1) == this && thisType == level.treeTypes[(x + 1) + (y - 1) * level.w]; + boolean isDownLeftTileSame = level.getTile(x - 1, y + 1) == this && thisType == level.treeTypes[(x - 1) + (y + 1) * level.w]; + boolean isDownRightTileSame = level.getTile(x + 1, y + 1) == this && thisType == level.treeTypes[(x + 1) + (y + 1) * level.w]; + + Sprite sprite = level.treeTypes[x + y * level.w].treeSprite.getSprite(); + Sprite spriteFull = level.treeTypes[x + y * level.w].treeSpriteFull.getSprite(); + + if (isUpTileSame && isUpLeftTileSame && isLeftTileSame) { + screen.render(x * 16 + 0, y * 16, spriteFull.spritePixels[0][1]); } else { - screen.render(x * 16 + 0, y * 16 + 0, sprite.spritePixels[0][0]); + screen.render(x * 16 + 0, y * 16, sprite.spritePixels[0][0]); } - if (u && ur && r) { - screen.render(x * 16 + 8, y * 16 + 0, spriteFull.spritePixels[0][0]); + if (isUpTileSame && isUpRightTileSame && isRightTileSame) { + screen.render(x * 16 + 8, y * 16, spriteFull.spritePixels[0][0]); } else { - screen.render(x * 16 + 8, y * 16 + 0, sprite.spritePixels[0][1]); + screen.render(x * 16 + 8, y * 16, sprite.spritePixels[0][1]); } - if (d && dl && l) { + if (isDownTileSame && isDownLeftTileSame && isLeftTileSame) { screen.render(x * 16 + 0, y * 16 + 8, spriteFull.spritePixels[1][1]); } else { screen.render(x * 16 + 0, y * 16 + 8, sprite.spritePixels[1][0]); } - if (d && dr && r) { + if (isDownTileSame && isDownRightTileSame && isRightTileSame) { screen.render(x * 16 + 8, y * 16 + 8, spriteFull.spritePixels[1][0]); } else { screen.render(x * 16 + 8, y * 16 + 8, sprite.spritePixels[1][1]); diff --git a/src/client/java/minicraft/level/tile/farming/CarrotTile.java b/src/client/java/minicraft/level/tile/farming/CarrotTile.java new file mode 100644 index 000000000..542fee7d6 --- /dev/null +++ b/src/client/java/minicraft/level/tile/farming/CarrotTile.java @@ -0,0 +1,28 @@ +package minicraft.level.tile.farming; + +import minicraft.gfx.Screen; +import minicraft.gfx.SpriteLinker.LinkedSprite; +import minicraft.gfx.SpriteLinker.SpriteType; +import minicraft.level.Level; +import minicraft.level.tile.Tiles; + +public class CarrotTile extends CropTile { + private final LinkedSprite[] spritStages = new LinkedSprite[] { + new LinkedSprite(SpriteType.Tile, "carrot_stage0"), + new LinkedSprite(SpriteType.Tile, "carrot_stage1"), + new LinkedSprite(SpriteType.Tile, "carrot_stage2"), + new LinkedSprite(SpriteType.Tile, "carrot_stage3") + }; + + public CarrotTile(String name) { + super(name, null); + } + + @Override + public void render(Screen screen, Level level, int x, int y) { + int age = (level.getData(x, y) >> 3) & maxAge; + Tiles.get("Farmland").render(screen, level, x, y); + int stage = (int) ((float) age / maxAge * 3); + screen.render(x * 16, y * 16, spritStages[stage]); + } +} diff --git a/src/client/java/minicraft/level/tile/farming/CropTile.java b/src/client/java/minicraft/level/tile/farming/CropTile.java new file mode 100644 index 000000000..89b368dd7 --- /dev/null +++ b/src/client/java/minicraft/level/tile/farming/CropTile.java @@ -0,0 +1,175 @@ +package minicraft.level.tile.farming; + +import minicraft.core.io.Sound; +import minicraft.entity.Direction; +import minicraft.entity.Entity; +import minicraft.entity.mob.Mob; +import minicraft.entity.mob.Player; +import minicraft.entity.particle.Particle; +import minicraft.gfx.SpriteLinker; +import minicraft.item.Item; +import minicraft.item.Items; +import minicraft.item.StackableItem; +import minicraft.level.Level; +import minicraft.level.tile.Tile; +import minicraft.level.tile.Tiles; +import minicraft.level.tile.WaterTile; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.Random; + +public class CropTile extends FarmTile { + protected final @Nullable String seed; + + protected int maxAge = 0b111; // Must be a bit mask. + + protected CropTile(String name, @Nullable String seed) { + super(name, null); + this.seed = seed; + } + + @Override + public boolean hurt(Level level, int x, int y, Mob source, int dmg, Direction attackDir) { + harvest(level, x, y, source); + return true; + } + + @Override + public boolean tick(Level level, int xt, int yt) { + int data = level.getData(xt, yt); + int moisture = data & 0b111; + boolean successful = false; + if (Arrays.stream(level.getAreaTiles(xt, yt, 4)).anyMatch(t -> t instanceof WaterTile)) { // Contains water. + if (moisture < 7 && random.nextInt(10) == 0) { // hydrating + level.setData(xt, yt, data = (data & ~0b111) + moisture++); + successful = true; + } + } else if (moisture > 0 && random.nextInt(10) == 0) { // drying + level.setData(xt, yt, data = (data & ~0b111) + moisture--); + successful = true; + } + + int fertilization = getFertilization(data); + int stage = (data >> 3) & maxAge; + if (stage < maxAge) { + double points = moisture > 0 ? 4 : 2; + for (int i = -1; i < 2; i++) + for (int j = -1; j < 2; j++) { + Tile t = level.getTile(xt + i, yt + j); + if ((i != 0 || j != 0) && t instanceof FarmTile) { + points += (level.getData(xt + i, yt + j) & 0b111) > 0 ? 0.75 : 0.25; + } + } + + // Checking whether the target direction has targeted the same CropTile + boolean up = level.getTile(xt, yt - 1) == this; + boolean down = level.getTile(xt, yt + 1) == this; + boolean left = level.getTile(xt - 1, yt) == this; + boolean right = level.getTile(xt + 1, yt) == this; + boolean upLeft = level.getTile(xt - 1, yt - 1) == this; + boolean downLeft = level.getTile(xt - 1, yt + 1) == this; + boolean upRight = level.getTile(xt + 1, yt - 1) == this; + boolean downRight = level.getTile(xt + 1, yt + 1) == this; + if (up && down && left && right && upLeft && downLeft && upRight && downRight) + points /= 2; + else { + if (up && down && left && right) + points *= 0.75; + if (up && (down && (left || right) || left && right) || down && left && right) // Either 3 of 4 directions. + points *= 0.85; + if (upLeft && (downRight || downLeft || upRight) || downLeft && (upRight || downRight) || upRight && downRight) // Either 2 of 4 directions. + points *= 0.9; + if (upLeft) points *= 0.98125; + if (downLeft) points *= 0.98125; + if (upRight) points *= 0.98125; + if (downRight) points *= 0.98125; + } + + if (random.nextInt((int) (100/points) + 1) < (fertilization/30 + 1)) // fertilization >= 0 + level.setData(xt, yt, data = (data & ~(maxAge << 3)) + ((stage + 1) << 3)); // Incrementing the stage by 1. + successful = true; + } + + if (fertilization > 0) { + level.setData(xt, yt, (data & (0b111 + (maxAge << 3))) + ((fertilization - 1) << (3 + (maxAge + 1)/2))); + successful = true; + } + + return successful; + } + + private static final SpriteLinker.LinkedSprite particleSprite = new SpriteLinker.LinkedSprite(SpriteLinker.SpriteType.Entity, "glint"); + + @Override + public boolean interact(Level level, int xt, int yt, Player player, Item item, Direction attackDir) { + if (item instanceof StackableItem && item.getName().equalsIgnoreCase("Fertilizer")) { + ((StackableItem) item).count--; + Random random = new Random(); + for (int i = 0; i < 2; ++i) { + double x = (double)xt * 16 + 8 + (random.nextGaussian() * 0.5) * 8; + double y = (double)yt * 16 + 8 + (random.nextGaussian() * 0.5) * 8; + level.add(new Particle((int) x, (int) y, 120 + random.nextInt(21) - 40, particleSprite)); + } + int fertilization = getFertilization(level.getData(xt, yt)); + if (fertilization < 100) { // More fertilization, lower the buffer is applied. + fertilize(level, xt, yt, 40); + } else if (fertilization < 200) { + fertilize(level, xt, yt, 30); + } else if (fertilization < 300) { + fertilize(level, xt, yt, 25); + } else if (fertilization < 400) { + fertilize(level, xt, yt, 20); + } else { + fertilize(level, xt, yt, 10); + } + + return true; + } + + return super.interact(level, xt, yt, player, item, attackDir); + } + + /** Default harvest method, used for everything that doesn't really need any special behavior. */ + protected void harvest(Level level, int x, int y, Entity entity) { + int data = level.getData(x, y); + int age = (data >> 3) & maxAge; + + if (seed != null) + level.dropItem(x * 16 + 8, y * 16 + 8, 1, Items.get(seed)); + + if (age == maxAge) { + level.dropItem(x * 16 + 8, y * 16 + 8, random.nextInt(3) + 2, Items.get(name)); + } else if (seed == null) { + level.dropItem(x * 16 + 8, y * 16 + 8, 1, Items.get(name)); + } + + if (age == maxAge && entity instanceof Player) { + ((Player)entity).addScore(random.nextInt(5) + 1); + } + + // Play sound. + Sound.play("monsterhurt"); + + level.setTile(x, y, Tiles.get("farmland"), data & 0b111); + } + + public int getFertilization(int data) { + return data >> (3 + (maxAge + 1)/2); + } + + /** + * Fertilization: Each magnitude of fertilization (by 1) increases the chance of growth by 1/30. + * (The addition by fertilization is rounded down to the nearest integer in chance calculation) + * For example, if the chance is originally 10% (1/10), the final chance with 30 fertilization will be 20% (2/10). + */ + public void fertilize(Level level, int x, int y, int amount) { + int data = level.getData(x, y); + int fertilization = getFertilization(data); + fertilization += amount; + if (fertilization < 0) fertilization = 0; + if (fertilization > 511) fertilization = 511; // The maximum possible value to be reached. + // If this value exceeds 511, the final value would be greater than the hard maximum value that short can be. + level.setData(x, y, (data & (0b111 + (maxAge << 3))) + (fertilization << (3 + (maxAge + 1)/2))); + } +} diff --git a/src/client/java/minicraft/level/tile/farming/FarmTile.java b/src/client/java/minicraft/level/tile/farming/FarmTile.java index 770cc0ead..c28bef21e 100644 --- a/src/client/java/minicraft/level/tile/farming/FarmTile.java +++ b/src/client/java/minicraft/level/tile/farming/FarmTile.java @@ -5,6 +5,7 @@ import minicraft.entity.Entity; import minicraft.entity.ItemEntity; import minicraft.entity.mob.Player; +import minicraft.gfx.Screen; import minicraft.gfx.SpriteAnimation; import minicraft.gfx.SpriteLinker.SpriteType; import minicraft.item.Item; @@ -13,17 +14,21 @@ import minicraft.level.Level; import minicraft.level.tile.Tile; import minicraft.level.tile.Tiles; +import minicraft.level.tile.WaterTile; import minicraft.util.AdvancementElement; +import java.util.Arrays; + public class FarmTile extends Tile { - private static SpriteAnimation sprite = new SpriteAnimation(SpriteType.Tile, "farmland"); + private static final SpriteAnimation sprite = new SpriteAnimation(SpriteType.Tile, "farmland"); + private static final SpriteAnimation spriteMoist = new SpriteAnimation(SpriteType.Tile, "farmland_moist"); - public FarmTile(String name) { - super(name, sprite); - } - protected FarmTile(String name, SpriteAnimation sprite) { - super(name, sprite); - } + public FarmTile(String name) { + super(name, sprite); + } + protected FarmTile(String name, SpriteAnimation sprite) { + super(name, sprite); + } @Override public boolean interact(Level level, int xt, int yt, Player player, Item item, Direction attackDir) { @@ -44,18 +49,30 @@ public boolean interact(Level level, int xt, int yt, Player player, Item item, D return false; } - @Override - public boolean tick(Level level, int xt, int yt) { - int age = level.getData(xt, yt); - if (age < 5) level.setData(xt, yt, age + 1); - return true; - } + @Override + public boolean tick(Level level, int xt, int yt) { + int moisture = level.getData(xt, yt) & 0b111; + if (Arrays.stream(level.getAreaTiles(xt, yt, 4)).anyMatch(t -> t instanceof WaterTile)) { // Contains water. + if (moisture < 7 && random.nextInt(10) == 0) { // hydrating + level.setData(xt, yt, moisture + 1); + return true; + } + } else if (moisture > 0 && random.nextInt(10) == 0) { // drying + level.setData(xt, yt, moisture - 1); + return true; + } else if (moisture == 0 && random.nextInt(10) == 0) { + level.setTile(xt, yt, Tiles.get("dirt")); + return true; + } - @Override - public void steppedOn(Level level, int xt, int yt, Entity entity) { - if (entity instanceof ItemEntity) return; - if (random.nextInt(60) != 0) return; - if (level.getData(xt, yt) < 5) return; - level.setTile(xt, yt, Tiles.get("Dirt")); - } + return false; + } + + @Override + public void render(Screen screen, Level level, int x, int y) { + if ((level.getData(x, y) & 0b111) > 0) + spriteMoist.render(screen, level, x, y); + else + sprite.render(screen, level, x, y); + } } diff --git a/src/client/java/minicraft/level/tile/farming/HeavenlyBerriesTile.java b/src/client/java/minicraft/level/tile/farming/HeavenlyBerriesTile.java new file mode 100644 index 000000000..c4fc57100 --- /dev/null +++ b/src/client/java/minicraft/level/tile/farming/HeavenlyBerriesTile.java @@ -0,0 +1,27 @@ +package minicraft.level.tile.farming; + +import minicraft.gfx.Screen; +import minicraft.gfx.SpriteLinker; +import minicraft.level.Level; +import minicraft.level.tile.Tiles; + +public class HeavenlyBerriesTile extends CropTile { + private final SpriteLinker.LinkedSprite[] spritStages = new SpriteLinker.LinkedSprite[] { + new SpriteLinker.LinkedSprite(SpriteLinker.SpriteType.Tile, "heavenly_berries_stage0"), + new SpriteLinker.LinkedSprite(SpriteLinker.SpriteType.Tile, "heavenly_berries_stage1"), + new SpriteLinker.LinkedSprite(SpriteLinker.SpriteType.Tile, "heavenly_berries_stage2"), + new SpriteLinker.LinkedSprite(SpriteLinker.SpriteType.Tile, "heavenly_berries_stage3") + }; + + public HeavenlyBerriesTile(String name) { + super(name, null); + } + + @Override + public void render(Screen screen, Level level, int x, int y) { + int age = (level.getData(x, y) >> 3) & maxAge; + Tiles.get("Farmland").render(screen, level, x, y); + int stage = (int) ((float) age / maxAge * 3); + screen.render(x * 16, y * 16, spritStages[stage]); + } +} diff --git a/src/client/java/minicraft/level/tile/farming/HellishBerriesTile.java b/src/client/java/minicraft/level/tile/farming/HellishBerriesTile.java new file mode 100644 index 000000000..6d4f7bcb5 --- /dev/null +++ b/src/client/java/minicraft/level/tile/farming/HellishBerriesTile.java @@ -0,0 +1,27 @@ +package minicraft.level.tile.farming; + +import minicraft.gfx.Screen; +import minicraft.gfx.SpriteLinker; +import minicraft.level.Level; +import minicraft.level.tile.Tiles; + +public class HellishBerriesTile extends CropTile { + private final SpriteLinker.LinkedSprite[] spritStages = new SpriteLinker.LinkedSprite[] { + new SpriteLinker.LinkedSprite(SpriteLinker.SpriteType.Tile, "hellish_berries_stage0"), + new SpriteLinker.LinkedSprite(SpriteLinker.SpriteType.Tile, "hellish_berries_stage1"), + new SpriteLinker.LinkedSprite(SpriteLinker.SpriteType.Tile, "hellish_berries_stage2"), + new SpriteLinker.LinkedSprite(SpriteLinker.SpriteType.Tile, "hellish_berries_stage3") + }; + + public HellishBerriesTile(String name) { + super(name, null); + } + + @Override + public void render(Screen screen, Level level, int x, int y) { + int age = (level.getData(x, y) >> 3) & maxAge; + Tiles.get("Farmland").render(screen, level, x, y); + int stage = (int) ((float) age / maxAge * 3); + screen.render(x * 16, y * 16, spritStages[stage]); + } +} diff --git a/src/client/java/minicraft/level/tile/farming/PlantTile.java b/src/client/java/minicraft/level/tile/farming/PlantTile.java deleted file mode 100644 index 0712acb29..000000000 --- a/src/client/java/minicraft/level/tile/farming/PlantTile.java +++ /dev/null @@ -1,84 +0,0 @@ -package minicraft.level.tile.farming; - -import minicraft.core.io.Sound; -import minicraft.entity.Direction; -import minicraft.entity.Entity; -import minicraft.entity.ItemEntity; -import minicraft.entity.mob.Mob; -import minicraft.entity.mob.MobAi; -import minicraft.entity.mob.Player; -import minicraft.item.Items; -import minicraft.level.Level; -import minicraft.level.tile.Tile; -import minicraft.level.tile.Tiles; - -public class PlantTile extends FarmTile { - protected static int maxAge = 100; - - protected PlantTile(String name) { - super(name, null); - } - - @Override - public void steppedOn(Level level, int xt, int yt, Entity entity) { - if (entity instanceof MobAi) return; - if (entity instanceof ItemEntity) return; - if (random.nextInt(60) != 0) return; - if (level.getData(xt, yt) < 5) return; - harvest(level, xt, yt, entity); - } - - @Override - public boolean hurt(Level level, int x, int y, Mob source, int dmg, Direction attackDir) { - harvest(level, x, y, source); - return true; - } - - @Override - public boolean tick(Level level, int xt, int yt) { - if (random.nextInt(2) == 0) return false; - - int age = level.getData(xt, yt); - if (age < maxAge) { - if (!IfWater(level, xt, yt)) level.setData(xt, yt, age + 1); - else if (IfWater(level, xt, yt)) level.setData(xt, yt, age + 2); - return true; - } - - return false; - } - - protected boolean IfWater(Level level, int xs, int ys) { - Tile[] areaTiles = level.getAreaTiles(xs, ys, 1); - for(Tile t: areaTiles) - if(t == Tiles.get("Water")) - return true; - - return false; - } - - /** Default harvest method, used for everything that doesn't really need any special behavior. */ - protected void harvest(Level level, int x, int y, Entity entity) { - int age = level.getData(x, y); - - level.dropItem(x * 16 + 8, y * 16 + 8, 1, Items.get(name + " Seeds")); - - int count = 0; - if (age >= maxAge) { - count = random.nextInt(3) + 2; - } else if (age >= maxAge - maxAge / 5) { - count = random.nextInt(2) + 1; - } - - level.dropItem(x * 16 + 8, y * 16 + 8, count, Items.get(name)); - - if (age >= maxAge && entity instanceof Player) { - ((Player)entity).addScore(random.nextInt(5) + 1); - } - - // Play sound. - Sound.play("monsterhurt"); - - level.setTile(x, y, Tiles.get("Dirt")); - } -} diff --git a/src/client/java/minicraft/level/tile/farming/PotatoTile.java b/src/client/java/minicraft/level/tile/farming/PotatoTile.java index a4c96fd49..9a5c44798 100644 --- a/src/client/java/minicraft/level/tile/farming/PotatoTile.java +++ b/src/client/java/minicraft/level/tile/farming/PotatoTile.java @@ -1,17 +1,13 @@ package minicraft.level.tile.farming; -import minicraft.core.io.Sound; -import minicraft.entity.Entity; -import minicraft.entity.mob.Player; import minicraft.gfx.Screen; import minicraft.gfx.SpriteLinker.LinkedSprite; import minicraft.gfx.SpriteLinker.SpriteType; -import minicraft.item.Items; import minicraft.level.Level; import minicraft.level.tile.Tiles; -public class PotatoTile extends PlantTile { - private LinkedSprite[] spritStages = new LinkedSprite[] { +public class PotatoTile extends CropTile { + private final LinkedSprite[] spritStages = new LinkedSprite[] { new LinkedSprite(SpriteType.Tile, "potato_stage0"), new LinkedSprite(SpriteType.Tile, "potato_stage1"), new LinkedSprite(SpriteType.Tile, "potato_stage2"), @@ -21,42 +17,14 @@ public class PotatoTile extends PlantTile { }; public PotatoTile(String name) { - super(name); - } - - static { - maxAge = 70; + super(name, null); } @Override public void render(Screen screen, Level level, int x, int y) { - int age = level.getData(x, y); - int icon = age / (maxAge / 5); - + int age = (level.getData(x, y) >> 3) & maxAge; Tiles.get("Farmland").render(screen, level, x, y); - screen.render(x * 16, y * 16, spritStages[icon]); - } - - @Override - protected void harvest(Level level, int x, int y, Entity entity) { - int age = level.getData(x, y); - - int count = 0; - if (age >= maxAge) { - count = random.nextInt(3) + 2; - } else if (age >= maxAge - maxAge / 5) { - count = random.nextInt(2); - } - - level.dropItem(x * 16 + 8, y * 16 + 8, count + 1, Items.get("Potato")); - - if (age >= maxAge && entity instanceof Player) { - ((Player)entity).addScore(random.nextInt(4) + 1); - } - - // Play sound. - Sound.play("monsterhurt"); - - level.setTile(x, y, Tiles.get("Dirt")); + int stage = (int) ((float) age / maxAge * 5); + screen.render(x * 16, y * 16, spritStages[stage]); } } diff --git a/src/client/java/minicraft/level/tile/farming/TomatoTile.java b/src/client/java/minicraft/level/tile/farming/TomatoTile.java new file mode 100644 index 000000000..a8329ae63 --- /dev/null +++ b/src/client/java/minicraft/level/tile/farming/TomatoTile.java @@ -0,0 +1,28 @@ +package minicraft.level.tile.farming; + +import minicraft.gfx.Screen; +import minicraft.gfx.SpriteLinker.LinkedSprite; +import minicraft.gfx.SpriteLinker.SpriteType; +import minicraft.level.Level; +import minicraft.level.tile.Tiles; + +public class TomatoTile extends CropTile { + private final LinkedSprite[] spritStages = new LinkedSprite[] { + new LinkedSprite(SpriteType.Tile, "tomato_stage0"), + new LinkedSprite(SpriteType.Tile, "tomato_stage1"), + new LinkedSprite(SpriteType.Tile, "tomato_stage2"), + new LinkedSprite(SpriteType.Tile, "tomato_stage3") + }; + + public TomatoTile(String name) { + super(name, "tomato seeds"); + } + + @Override + public void render(Screen screen, Level level, int x, int y) { + int age = (level.getData(x, y) >> 3) & maxAge; + Tiles.get("Farmland").render(screen, level, x, y); + int stage = (int) ((float) age / maxAge * 3); + screen.render(x * 16, y * 16, spritStages[stage]); + } +} diff --git a/src/client/java/minicraft/level/tile/farming/WheatTile.java b/src/client/java/minicraft/level/tile/farming/WheatTile.java index f5f3c6cfb..39f585bf4 100644 --- a/src/client/java/minicraft/level/tile/farming/WheatTile.java +++ b/src/client/java/minicraft/level/tile/farming/WheatTile.java @@ -6,8 +6,8 @@ import minicraft.level.Level; import minicraft.level.tile.Tiles; -public class WheatTile extends PlantTile { - private LinkedSprite[] spritStages = new LinkedSprite[] { +public class WheatTile extends CropTile { + private final LinkedSprite[] spritStages = new LinkedSprite[] { new LinkedSprite(SpriteType.Tile, "wheat_stage0"), new LinkedSprite(SpriteType.Tile, "wheat_stage1"), new LinkedSprite(SpriteType.Tile, "wheat_stage2"), @@ -17,15 +17,14 @@ public class WheatTile extends PlantTile { }; public WheatTile(String name) { - super(name); + super(name, "wheat seeds"); } @Override public void render(Screen screen, Level level, int x, int y) { - int age = level.getData(x, y); - int icon = age / (maxAge / 5); - + int age = (level.getData(x, y) >> 3) & maxAge; Tiles.get("Farmland").render(screen, level, x, y); - screen.render(x * 16, y * 16, spritStages[icon]); + int stage = (int) ((float) age / maxAge * 5); + screen.render(x * 16, y * 16, spritStages[stage]); } } diff --git a/src/client/java/minicraft/saveload/Load.java b/src/client/java/minicraft/saveload/Load.java index 03b06c5b2..7909495b3 100644 --- a/src/client/java/minicraft/saveload/Load.java +++ b/src/client/java/minicraft/saveload/Load.java @@ -315,7 +315,7 @@ private static ArrayList splitUnwrappedCommas(String input) { if (ch == commaChar && bracketCounter.isEmpty()) { String str = input.substring(lastIdx + (input.charAt(lastIdx) == commaChar ? 1 : 0), i).trim(); lastIdx = i; - if (!str.isEmpty()) out.add(str); + out.add(str); // Empty strings are expected. } else if (ch == openBracket0) { bracketCounter.push(0); } else if (ch == closeBracket0) { diff --git a/src/client/resources/assets/localization/en-us.json b/src/client/resources/assets/localization/en-us.json index 175485dfe..22a2d1de9 100644 --- a/src/client/resources/assets/localization/en-us.json +++ b/src/client/resources/assets/localization/en-us.json @@ -31,6 +31,8 @@ "minicraft.achievement.airwizard.desc": "Defeat the first Air Wizard!", "minicraft.achievement.skin": "Fashion Show", "minicraft.achievement.skin.desc": "Change your skin.", + "minicraft.achievement.plant_seed": "A Seedy Place", + "minicraft.achievement.plant_seed.desc": "Plant a seed and watch it grow.", "minicraft.control_guide.attack": "Use %s to attack mobs or destroy tiles.", "minicraft.control_guide.craft": "Use %s to open your crafting menu.", "minicraft.control_guide.menu": "Use %s to open your inventory menu.", @@ -233,6 +235,7 @@ "minicraft.notification.wrong_level_dungeon": "Can only be summoned on the dungeon level", "minicraft.notification.boss_limit": "No more bosses can be spawned", "minicraft.notification.spawn_on_boss_tile": "Can only be summoned in the Boss Room", + "minicraft.notification.knight_statue_exists": "A knight statue exists", "minicraft.notifications.statue_tapped": "You hear echoed whispers...", "minicraft.notifications.statue_touched": "You hear the statue vibrating...", "minicraft.quest.farming": "Farming Farmer", diff --git a/src/client/resources/assets/textures/entity/composter.png b/src/client/resources/assets/textures/entity/composter.png new file mode 100644 index 000000000..7a97ee3de Binary files /dev/null and b/src/client/resources/assets/textures/entity/composter.png differ diff --git a/src/client/resources/assets/textures/entity/compostor_filled.png b/src/client/resources/assets/textures/entity/compostor_filled.png new file mode 100644 index 000000000..0b230a9df Binary files /dev/null and b/src/client/resources/assets/textures/entity/compostor_filled.png differ diff --git a/src/client/resources/assets/textures/entity/compostor_full.png b/src/client/resources/assets/textures/entity/compostor_full.png new file mode 100644 index 000000000..f4e360244 Binary files /dev/null and b/src/client/resources/assets/textures/entity/compostor_full.png differ diff --git a/src/client/resources/assets/textures/entity/glint.png b/src/client/resources/assets/textures/entity/glint.png new file mode 100644 index 000000000..3b14f6d8f Binary files /dev/null and b/src/client/resources/assets/textures/entity/glint.png differ diff --git a/src/client/resources/assets/textures/entity/splash_0.png b/src/client/resources/assets/textures/entity/splash_0.png new file mode 100644 index 000000000..8065908bd Binary files /dev/null and b/src/client/resources/assets/textures/entity/splash_0.png differ diff --git a/src/client/resources/assets/textures/entity/splash_1.png b/src/client/resources/assets/textures/entity/splash_1.png new file mode 100644 index 000000000..f95a7be19 Binary files /dev/null and b/src/client/resources/assets/textures/entity/splash_1.png differ diff --git a/src/client/resources/assets/textures/entity/splash_2.png b/src/client/resources/assets/textures/entity/splash_2.png new file mode 100644 index 000000000..588d463be Binary files /dev/null and b/src/client/resources/assets/textures/entity/splash_2.png differ diff --git a/src/client/resources/assets/textures/entity/splash_3.png b/src/client/resources/assets/textures/entity/splash_3.png new file mode 100644 index 000000000..fcec23a26 Binary files /dev/null and b/src/client/resources/assets/textures/entity/splash_3.png differ diff --git a/src/client/resources/assets/textures/item/arcane_fertilizer.png b/src/client/resources/assets/textures/item/arcane_fertilizer.png new file mode 100644 index 000000000..8b457a0ef Binary files /dev/null and b/src/client/resources/assets/textures/item/arcane_fertilizer.png differ diff --git a/src/client/resources/assets/textures/item/bone_meal.png b/src/client/resources/assets/textures/item/bone_meal.png new file mode 100644 index 000000000..c75a8b731 Binary files /dev/null and b/src/client/resources/assets/textures/item/bone_meal.png differ diff --git a/src/client/resources/assets/textures/item/carrot.png b/src/client/resources/assets/textures/item/carrot.png new file mode 100644 index 000000000..a23486c18 Binary files /dev/null and b/src/client/resources/assets/textures/item/carrot.png differ diff --git a/src/client/resources/assets/textures/item/composter.png b/src/client/resources/assets/textures/item/composter.png new file mode 100644 index 000000000..9ba7a47e9 Binary files /dev/null and b/src/client/resources/assets/textures/item/composter.png differ diff --git a/src/client/resources/assets/textures/item/fertilizer.png b/src/client/resources/assets/textures/item/fertilizer.png new file mode 100644 index 000000000..699e9a04d Binary files /dev/null and b/src/client/resources/assets/textures/item/fertilizer.png differ diff --git a/src/client/resources/assets/textures/item/heavenly_berries.png b/src/client/resources/assets/textures/item/heavenly_berries.png new file mode 100644 index 000000000..268bbfcd3 Binary files /dev/null and b/src/client/resources/assets/textures/item/heavenly_berries.png differ diff --git a/src/client/resources/assets/textures/item/hellish_berries.png b/src/client/resources/assets/textures/item/hellish_berries.png new file mode 100644 index 000000000..cfd64b963 Binary files /dev/null and b/src/client/resources/assets/textures/item/hellish_berries.png differ diff --git a/src/client/resources/assets/textures/item/tomato.png b/src/client/resources/assets/textures/item/tomato.png new file mode 100644 index 000000000..3d5638a89 Binary files /dev/null and b/src/client/resources/assets/textures/item/tomato.png differ diff --git a/src/client/resources/assets/textures/item/watering_can.png b/src/client/resources/assets/textures/item/watering_can.png new file mode 100644 index 000000000..263ce2749 Binary files /dev/null and b/src/client/resources/assets/textures/item/watering_can.png differ diff --git a/src/client/resources/assets/textures/item/watering_can_filled.png b/src/client/resources/assets/textures/item/watering_can_filled.png new file mode 100644 index 000000000..54d42cc09 Binary files /dev/null and b/src/client/resources/assets/textures/item/watering_can_filled.png differ diff --git a/src/client/resources/assets/textures/tile/ash.png b/src/client/resources/assets/textures/tile/ash.png new file mode 100644 index 000000000..526e17335 Binary files /dev/null and b/src/client/resources/assets/textures/tile/ash.png differ diff --git a/src/client/resources/assets/textures/tile/ash_full.png b/src/client/resources/assets/textures/tile/ash_full.png new file mode 100644 index 000000000..f1e9066f1 Binary files /dev/null and b/src/client/resources/assets/textures/tile/ash_full.png differ diff --git a/src/client/resources/assets/textures/tile/aspen.png b/src/client/resources/assets/textures/tile/aspen.png new file mode 100644 index 000000000..42d1ee24d Binary files /dev/null and b/src/client/resources/assets/textures/tile/aspen.png differ diff --git a/src/client/resources/assets/textures/tile/aspen_full.png b/src/client/resources/assets/textures/tile/aspen_full.png new file mode 100644 index 000000000..81ac0cb25 Binary files /dev/null and b/src/client/resources/assets/textures/tile/aspen_full.png differ diff --git a/src/client/resources/assets/textures/tile/birch.png b/src/client/resources/assets/textures/tile/birch.png new file mode 100644 index 000000000..6022db651 Binary files /dev/null and b/src/client/resources/assets/textures/tile/birch.png differ diff --git a/src/client/resources/assets/textures/tile/birch_full.png b/src/client/resources/assets/textures/tile/birch_full.png new file mode 100644 index 000000000..2b65e2e55 Binary files /dev/null and b/src/client/resources/assets/textures/tile/birch_full.png differ diff --git a/src/client/resources/assets/textures/tile/carrot_stage0.png b/src/client/resources/assets/textures/tile/carrot_stage0.png new file mode 100644 index 000000000..edace2a7b Binary files /dev/null and b/src/client/resources/assets/textures/tile/carrot_stage0.png differ diff --git a/src/client/resources/assets/textures/tile/carrot_stage1.png b/src/client/resources/assets/textures/tile/carrot_stage1.png new file mode 100644 index 000000000..19d4ab299 Binary files /dev/null and b/src/client/resources/assets/textures/tile/carrot_stage1.png differ diff --git a/src/client/resources/assets/textures/tile/carrot_stage2.png b/src/client/resources/assets/textures/tile/carrot_stage2.png new file mode 100644 index 000000000..8426131fe Binary files /dev/null and b/src/client/resources/assets/textures/tile/carrot_stage2.png differ diff --git a/src/client/resources/assets/textures/tile/carrot_stage3.png b/src/client/resources/assets/textures/tile/carrot_stage3.png new file mode 100644 index 000000000..68b50001c Binary files /dev/null and b/src/client/resources/assets/textures/tile/carrot_stage3.png differ diff --git a/src/client/resources/assets/textures/tile/farmland_moist.png b/src/client/resources/assets/textures/tile/farmland_moist.png new file mode 100644 index 000000000..ce33ea207 Binary files /dev/null and b/src/client/resources/assets/textures/tile/farmland_moist.png differ diff --git a/src/client/resources/assets/textures/tile/fern.png b/src/client/resources/assets/textures/tile/fern.png new file mode 100644 index 000000000..67e5cb1b8 Binary files /dev/null and b/src/client/resources/assets/textures/tile/fern.png differ diff --git a/src/client/resources/assets/textures/tile/fir.png b/src/client/resources/assets/textures/tile/fir.png new file mode 100644 index 000000000..db594a536 Binary files /dev/null and b/src/client/resources/assets/textures/tile/fir.png differ diff --git a/src/client/resources/assets/textures/tile/fir_full.png b/src/client/resources/assets/textures/tile/fir_full.png new file mode 100644 index 000000000..fb1ce243a Binary files /dev/null and b/src/client/resources/assets/textures/tile/fir_full.png differ diff --git a/src/client/resources/assets/textures/tile/heavenly_berries_stage0.png b/src/client/resources/assets/textures/tile/heavenly_berries_stage0.png new file mode 100644 index 000000000..acfde05ba Binary files /dev/null and b/src/client/resources/assets/textures/tile/heavenly_berries_stage0.png differ diff --git a/src/client/resources/assets/textures/tile/heavenly_berries_stage1.png b/src/client/resources/assets/textures/tile/heavenly_berries_stage1.png new file mode 100644 index 000000000..451d6308b Binary files /dev/null and b/src/client/resources/assets/textures/tile/heavenly_berries_stage1.png differ diff --git a/src/client/resources/assets/textures/tile/heavenly_berries_stage2.png b/src/client/resources/assets/textures/tile/heavenly_berries_stage2.png new file mode 100644 index 000000000..630098f8b Binary files /dev/null and b/src/client/resources/assets/textures/tile/heavenly_berries_stage2.png differ diff --git a/src/client/resources/assets/textures/tile/heavenly_berries_stage3.png b/src/client/resources/assets/textures/tile/heavenly_berries_stage3.png new file mode 100644 index 000000000..ad0d0ae99 Binary files /dev/null and b/src/client/resources/assets/textures/tile/heavenly_berries_stage3.png differ diff --git a/src/client/resources/assets/textures/tile/hellish_berries_stage0.png b/src/client/resources/assets/textures/tile/hellish_berries_stage0.png new file mode 100644 index 000000000..3a7271992 Binary files /dev/null and b/src/client/resources/assets/textures/tile/hellish_berries_stage0.png differ diff --git a/src/client/resources/assets/textures/tile/hellish_berries_stage1.png b/src/client/resources/assets/textures/tile/hellish_berries_stage1.png new file mode 100644 index 000000000..25af95129 Binary files /dev/null and b/src/client/resources/assets/textures/tile/hellish_berries_stage1.png differ diff --git a/src/client/resources/assets/textures/tile/hellish_berries_stage2.png b/src/client/resources/assets/textures/tile/hellish_berries_stage2.png new file mode 100644 index 000000000..e75593447 Binary files /dev/null and b/src/client/resources/assets/textures/tile/hellish_berries_stage2.png differ diff --git a/src/client/resources/assets/textures/tile/hellish_berries_stage3.png b/src/client/resources/assets/textures/tile/hellish_berries_stage3.png new file mode 100644 index 000000000..379d2eea0 Binary files /dev/null and b/src/client/resources/assets/textures/tile/hellish_berries_stage3.png differ diff --git a/src/client/resources/assets/textures/tile/large_fern.png b/src/client/resources/assets/textures/tile/large_fern.png new file mode 100644 index 000000000..f5edb4c5c Binary files /dev/null and b/src/client/resources/assets/textures/tile/large_fern.png differ diff --git a/src/client/resources/assets/textures/tile/oak.png b/src/client/resources/assets/textures/tile/oak.png new file mode 100644 index 000000000..05c9bfd16 Binary files /dev/null and b/src/client/resources/assets/textures/tile/oak.png differ diff --git a/src/client/resources/assets/textures/tile/oak_full.png b/src/client/resources/assets/textures/tile/oak_full.png new file mode 100644 index 000000000..2b0b206e9 Binary files /dev/null and b/src/client/resources/assets/textures/tile/oak_full.png differ diff --git a/src/client/resources/assets/textures/tile/spruce.png b/src/client/resources/assets/textures/tile/spruce.png new file mode 100644 index 000000000..e51fbb524 Binary files /dev/null and b/src/client/resources/assets/textures/tile/spruce.png differ diff --git a/src/client/resources/assets/textures/tile/spruce_full.png b/src/client/resources/assets/textures/tile/spruce_full.png new file mode 100644 index 000000000..d3ea3dbf0 Binary files /dev/null and b/src/client/resources/assets/textures/tile/spruce_full.png differ diff --git a/src/client/resources/assets/textures/tile/tomato_stage0.png b/src/client/resources/assets/textures/tile/tomato_stage0.png new file mode 100644 index 000000000..1ceea3e4d Binary files /dev/null and b/src/client/resources/assets/textures/tile/tomato_stage0.png differ diff --git a/src/client/resources/assets/textures/tile/tomato_stage1.png b/src/client/resources/assets/textures/tile/tomato_stage1.png new file mode 100644 index 000000000..0cd3ff91b Binary files /dev/null and b/src/client/resources/assets/textures/tile/tomato_stage1.png differ diff --git a/src/client/resources/assets/textures/tile/tomato_stage2.png b/src/client/resources/assets/textures/tile/tomato_stage2.png new file mode 100644 index 000000000..24e990449 Binary files /dev/null and b/src/client/resources/assets/textures/tile/tomato_stage2.png differ diff --git a/src/client/resources/assets/textures/tile/tomato_stage3.png b/src/client/resources/assets/textures/tile/tomato_stage3.png new file mode 100644 index 000000000..80fbf4c2b Binary files /dev/null and b/src/client/resources/assets/textures/tile/tomato_stage3.png differ diff --git a/src/client/resources/assets/textures/tile/tree.png b/src/client/resources/assets/textures/tile/tree.png deleted file mode 100644 index d93017de5..000000000 Binary files a/src/client/resources/assets/textures/tile/tree.png and /dev/null differ diff --git a/src/client/resources/assets/textures/tile/tree_full.png b/src/client/resources/assets/textures/tile/tree_full.png deleted file mode 100644 index 8dad3ec38..000000000 Binary files a/src/client/resources/assets/textures/tile/tree_full.png and /dev/null differ diff --git a/src/client/resources/assets/textures/tile/willow.png b/src/client/resources/assets/textures/tile/willow.png new file mode 100644 index 000000000..88f43ce2a Binary files /dev/null and b/src/client/resources/assets/textures/tile/willow.png differ diff --git a/src/client/resources/assets/textures/tile/willow_full.png b/src/client/resources/assets/textures/tile/willow_full.png new file mode 100644 index 000000000..ef7ad2e03 Binary files /dev/null and b/src/client/resources/assets/textures/tile/willow_full.png differ diff --git a/src/client/resources/resources/achievements.json b/src/client/resources/resources/achievements.json index 307f8a20b..99fbc9027 100644 --- a/src/client/resources/resources/achievements.json +++ b/src/client/resources/resources/achievements.json @@ -78,5 +78,10 @@ "id": "minicraft.achievement.skin", "desc": "minicraft.achievement.skin.desc", "score": 10 + }, + { + "id": "minicraft.achievement.plant_seed", + "desc": "minicraft.achievement.plant_seed.desc", + "score": 20 } ]