Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add sign #548

Merged
merged 21 commits into from
Apr 2, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/client/java/minicraft/core/Renderer.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import minicraft.screen.Menu;
import minicraft.screen.QuestsDisplay;
import minicraft.screen.RelPos;
import minicraft.screen.SignDisplayMenu;
import minicraft.screen.TutorialDisplayHandler;
import minicraft.screen.entry.ListEntry;
import minicraft.screen.entry.StringEntry;
Expand Down Expand Up @@ -73,6 +74,8 @@ private Renderer() {
public static boolean readyToRenderGameplay = false;
public static boolean showDebugInfo = false;

public static SignDisplayMenu signDisplayMenu = null;

private static Ellipsis ellipsis = new SmoothEllipsis(new TickUpdater());

private static int potionRenderOffset = 0;
Expand Down Expand Up @@ -421,6 +424,7 @@ private static void renderGui() {

TutorialDisplayHandler.render(screen);
renderQuestsDisplay();
if (signDisplayMenu != null) signDisplayMenu.render(screen);
renderDebugInfo();
}

Expand Down
4 changes: 4 additions & 0 deletions src/client/java/minicraft/core/World.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import minicraft.screen.LoadingDisplay;
import minicraft.screen.PlayerDeathDisplay;
import minicraft.screen.QuestsDisplay;
import minicraft.screen.SignDisplay;
import minicraft.screen.TutorialDisplayHandler;
import minicraft.screen.WorldGenDisplay;
import minicraft.screen.WorldSelectDisplay;
Expand Down Expand Up @@ -159,10 +160,13 @@ public static void resetGame(boolean keepPlayer) {
CraftingDisplay.resetRecipeUnlocks();
TutorialDisplayHandler.reset(true);
AdvancementElement.resetRecipeUnlockingElements();
SignDisplay.resetSignTexts();
}

Renderer.readyToRenderGameplay = true;

Renderer.signDisplayMenu = null;

PlayerDeathDisplay.shouldRespawn = true;

Logging.WORLD.trace("World initialized.");
Expand Down
43 changes: 29 additions & 14 deletions src/client/java/minicraft/core/io/InputHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -642,41 +642,56 @@ public void keyTyped(KeyEvent ke) {
keyTypedBuffer = String.valueOf(ke.getKeyChar());
}

private static final String control = "\\p{Print}"; // Should match only printable characters.

private static final String control = "[\\p{Print}\n]+"; // Should match only printable characters.
public String addKeyTyped(String typing, @Nullable String pattern) {
return handleBackspaceChars(getKeysTyped(typing, pattern));
}

/** This returns a raw format of the keys typed, i.e. {@code \b} are not handled here. */
public String getKeysTyped(@Nullable String pattern) { return getKeysTyped(null, pattern, true); }
public String getKeysTyped(@Nullable String typing, @Nullable String pattern) { return getKeysTyped(typing, pattern, false); }
public String getKeysTyped(@Nullable String typing, @Nullable String pattern, boolean multiline) {
StringBuilder typed = typing == null ? new StringBuilder() : new StringBuilder(typing);
if (lastKeyTyped.length() > 0) {
String letter = lastKeyTyped;
for (char letter : lastKeyTyped.toCharArray()) {
String letterString = String.valueOf(letter);
if (letter == '\b' || letterString.matches(control) && (letter != '\n' || multiline) && (pattern == null || letterString.matches(pattern)))
typed.append(letter);
}
lastKeyTyped = "";
if (letter.matches(control) && (pattern == null || letter.matches(pattern)) || letter.equals("\b"))
typing += letter;
}

return typed.toString();
}

public static String handleBackspaceChars(String typing) { return handleBackspaceChars(typing, false); }
/**
* This handles and erases backspace control characters {@code \b} from the given string.
* Evaluation to backspace characters stops if no more characters are in front of them.
* @param keepUnevaluated {@code true} if intending to keep the unhandled backspace characters in the returned string;
* otherwise, those characters are removed even that they are not evaluated.
*/
public static String handleBackspaceChars(String typing, boolean keepUnevaluated) {
// Erasing characters by \b. Reference: https://stackoverflow.com/a/30174028
Stack<Character> stack = new Stack<>();

// for-each character in the string
for (int i = 0; i < typing.length(); i++) {
char c = typing.charAt(i);

// push if it's not a backspace
if (c != '\b') {
stack.push(c);
// else pop if possible
} else if (!stack.empty()) {
if (c == '\b' && !stack.empty() && stack.peek() != '\b') { // pop if the last char exists and is not \b
stack.pop();
} else if (c != '\b' || keepUnevaluated) {
stack.push(c);
}
}

// convert stack to string
StringBuilder builder = new StringBuilder(stack.size());

for (Character c : stack) {
builder.append(c);
}

typing = builder.toString();
return typing;
return builder.toString();
}

public boolean anyControllerConnected() {
Expand Down
11 changes: 10 additions & 1 deletion src/client/java/minicraft/entity/Entity.java
Original file line number Diff line number Diff line change
Expand Up @@ -162,11 +162,20 @@ public boolean interact(Player player, @Nullable Item item, Direction attackDir)
public boolean move(int xd, int yd) {
if (Updater.saving || (xd == 0 && yd == 0)) return true; // Pretend that it kept moving

int prevXt = x >> 4;
int prevYt = y >> 4;

boolean stopped = true; // Used to check if the entity has BEEN stopped, COMPLETELY; below checks for a lack of collision.
//noinspection RedundantIfStatement
if (moveX(xd)) stopped = false; // Becomes false if horizontal movement was successful.
if (moveY(yd)) stopped = false; // Becomes false if vertical movement was successful.

if (!stopped) {
int xt = x >> 4; // The x tile coordinate that the entity is standing on.
int yt = y >> 4; // The y tile coordinate that the entity is standing on.
if (prevXt != xt || prevYt != yt)
level.getTile(prevXt, prevYt).steppedOut(level, prevXt, prevYt, this);
level.getTile(xt, yt).steppedOn(level, xt, yt, this); // Calls the steppedOn() method in a tile's class. (used for tiles like sand (footprints) or lava (burning))
}
return !stopped;
}

Expand Down
13 changes: 13 additions & 0 deletions src/client/java/minicraft/gfx/Color.java
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,19 @@ protected static int[] decodeRGBColor(int rgbInt) {
return new int[]{r, g, b};
}

/**
* Gets the lightness of the given 24-bit RGB color value.
* This is strictly calculated by L from RGB to HSL conversion.
* For other formula and method reference: https://stackoverflow.com/a/56678483.
* @return lightness, from 0 to 1 floating point number
*/
public static float getLightnessFromRGB(int color) {
int r = (color >> 16) & 0xFF;
int g = (color >> 8) & 0xFF;
int b = color & 0xFF;
return (Math.max(Math.max(r, g), b) + Math.min(Math.min(r, g), b)) / 510f;
}

/// This is for color testing.
public static void main(String[] args) {
int r, g, b;
Expand Down
1 change: 1 addition & 0 deletions src/client/java/minicraft/item/Items.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ private static void addAll(ArrayList<Item> items) {
addAll(SummonItem.getAllInstances());
addAll(HeartItem.getAllInstances());
addAll(WateringCanItem.getAllInstances());
addAll(SignItem.getAllInstances());
}

public static ArrayList<Item> getAll() {
Expand Down
42 changes: 42 additions & 0 deletions src/client/java/minicraft/item/SignItem.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package minicraft.item;

import java.util.ArrayList;

import minicraft.core.Game;
import minicraft.entity.Direction;
import minicraft.entity.mob.Player;
import minicraft.gfx.Sprite;
import minicraft.gfx.SpriteLinker;
import minicraft.level.Level;
import minicraft.level.tile.SignTile;
import minicraft.level.tile.Tile;
import minicraft.screen.SignDisplay;
import org.jetbrains.annotations.NotNull;

public class SignItem extends TileItem {
private static final SpriteLinker.LinkedSprite sprite = new SpriteLinker.LinkedSprite(SpriteLinker.SpriteType.Item, "sign");

public static ArrayList<Item> getAllInstances() {
ArrayList<Item> items = new ArrayList<>();
items.add(new SignItem());
return items;
}

private SignItem() { this(1); }
private SignItem(int count) {
super("Sign", sprite, count, null, "dirt", "Wood Planks", "Stone Bricks", "Obsidian", "Wool", "Red Wool", "Blue Wool", "Green Wool", "Yellow Wool", "Black Wool", "grass", "sand");
}

public boolean interactOn(Tile tile, Level level, int xt, int yt, Player player, Direction attackDir) {
if (validTiles.contains(tile.name)) {
level.setTile(xt, yt, SignTile.getSignTile(tile));
Game.setDisplay(new SignDisplay(level, xt, yt));
return super.interactOn(true);
}
return super.interactOn(false);
}

public @NotNull SignItem copy() {
return new SignItem(count);
}
}
2 changes: 1 addition & 1 deletion src/client/java/minicraft/level/Level.java
Original file line number Diff line number Diff line change
Expand Up @@ -560,7 +560,7 @@ private void sortAndRender(Screen screen, List<Entity> list) {
public Tile getTile(int x, int y) {
if (x < 0 || y < 0 || x >= w || y >= h /* || (x + y * w) >= tiles.length*/) return Tiles.get("connector tile");
int id = tiles[x + y * w];
if (id < 0) id += 256;
if (id < 0) id += 32768;
return Tiles.get(id);
}

Expand Down
100 changes: 100 additions & 0 deletions src/client/java/minicraft/level/tile/SignTile.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package minicraft.level.tile;

import minicraft.core.Game;
import minicraft.core.Renderer;
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.gfx.Screen;
import minicraft.gfx.SpriteAnimation;
import minicraft.gfx.SpriteLinker;
import minicraft.item.Item;
import minicraft.item.Items;
import minicraft.item.PowerGloveItem;
import minicraft.item.ToolItem;
import minicraft.item.ToolType;
import minicraft.level.Level;
import minicraft.screen.SignDisplay;
import minicraft.screen.SignDisplayMenu;
import org.jetbrains.annotations.Nullable;
import org.tinylog.Logger;

public class SignTile extends Tile {
private static final SpriteAnimation sprite = new SpriteAnimation(SpriteLinker.SpriteType.Tile, "sign");

private final Tile onType;

public static SignTile getSignTile(Tile onTile) {
int id = onTile.id & 0xFFFF;
if(id < 16384) id += 16384;
else Logger.tag("SignTile").info("Tried to place torch on torch or sign tile...");

if(Tiles.containsTile(id)) {
return (SignTile)Tiles.get(id);
} else {
SignTile tile = new SignTile(onTile);
Tiles.add(id, tile);
return tile;
}
}

private SignTile(Tile onType) {
super("Sign "+ onType.name, sprite);
this.onType = onType;
this.connectsToSand = onType.connectsToSand;
this.connectsToGrass = onType.connectsToGrass;
this.connectsToFluid = onType.connectsToFluid;
}

public void render(Screen screen, Level level, int x, int y) {
onType.render(screen, level, x, y);
sprite.render(screen, level, x, y);
}

public boolean interact(Level level, int xt, int yt, Player player, Item item, Direction attackDir) {
if (item != null) {
if (item instanceof ToolItem && ((ToolItem) item).type == ToolType.Axe) {
level.setTile(xt, yt, this.onType);
SignDisplay.removeSign(level.depth, xt, yt);
Sound.play("monsterhurt");
level.dropItem(xt*16+8, yt*16+8, Items.get("Sign"));
return true;
}
} else { // TODO Add a way to lock signs
Game.setDisplay(new SignDisplay(level, xt, yt));
return true;
}

return false;
}

@Override
public boolean hurt(Level level, int x, int y, Mob source, int dmg, Direction attackDir) {
if (source instanceof Player) {
Game.setDisplay(new SignDisplay(level, x, y));
return true;
}

return false;
}

@Override
public void steppedOn(Level level, int xt, int yt, Entity entity) {
if (entity instanceof Player) {
if (Renderer.signDisplayMenu == null || Renderer.signDisplayMenu.differsFrom(level.depth, xt, yt)) {
Renderer.signDisplayMenu = new SignDisplayMenu(level, xt, yt);
}
}
}

@Override
public void steppedOut(Level level, int xt, int yt, Entity entity) {
if (entity instanceof Player) {
if (Renderer.signDisplayMenu != null && Renderer.signDisplayMenu.matches(level.depth, xt, yt)) {
Renderer.signDisplayMenu = null;
}
}
}
}
3 changes: 3 additions & 0 deletions src/client/java/minicraft/level/tile/Tile.java
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ public boolean tick(Level level, int xt, int yt) {
public void steppedOn(Level level, int xt, int yt, Entity entity) {
}

/** What happens when you have just stepped out the tile (ex: sign) */
public void steppedOut(Level level, int xt, int yt, Entity entity) {}

/**
* Called when you hit an item on a tile (ex: Pickaxe on rock).
*
Expand Down
10 changes: 10 additions & 0 deletions src/client/java/minicraft/level/tile/Tiles.java
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,12 @@ public static Tile get(String name) {
name = name.substring(6); // Cuts off torch prefix.
}

boolean isSign = false;
if (name.startsWith("SIGN")) {
isSign = true;
name = name.substring(5);
}

if (name.contains("_")) {
name = name.substring(0, name.indexOf("_"));
}
Expand All @@ -234,6 +240,10 @@ public static Tile get(String name) {
getting = TorchTile.getTorchTile(getting);
}

if (isSign) {
getting = SignTile.getSignTile(getting);
}

overflowCheck = 0;
return getting;
}
Expand Down
2 changes: 1 addition & 1 deletion src/client/java/minicraft/level/tile/TorchTile.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class TorchTile extends Tile {
public static TorchTile getTorchTile(Tile onTile) {
int id = onTile.id & 0xFFFF;
if (id < 16384) id += 16384;
else Logger.tag("TorchTile").info("Tried to place torch on torch tile...");
else Logger.tag("TorchTile").info("Tried to place torch on torch or sign tile...");

if (Tiles.containsTile(id))
return (TorchTile) Tiles.get(id);
Expand Down
Loading
Loading