diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/config/AntarchyConfigCatalog.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/config/AntarchyConfigCatalog.java index e755b035..1df735f3 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/config/AntarchyConfigCatalog.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/config/AntarchyConfigCatalog.java @@ -144,6 +144,7 @@ public final class AntarchyConfigCatalog { || name.equals("swingThroughGrassEnabled") || name.equals("fabricKeybindingConflictFixEnabled") || name.equals("experimentalSettingsPopupDisabled") + || name.equals("unlockAllArchives") || name.equals("entitySpecificFireOverlayEnabled") || name.equals("dreamSandEnabled") || name.startsWith("dreamSand") diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/config/AntarchySettings.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/config/AntarchySettings.java index a24bd81b..1933c01c 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/config/AntarchySettings.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/config/AntarchySettings.java @@ -24,6 +24,7 @@ public final class AntarchySettings { private static boolean swingThroughGrassEnabled = true; private static boolean fabricKeybindingConflictFixEnabled = true; private static boolean experimentalSettingsPopupDisabled = true; + private static boolean unlockAllArchives = true; private static boolean sizeChangingRaysEnabled = true; private static double sizeRayMinScale = 0.1D; private static double sizeRayMaxScale = 10.0D; @@ -696,6 +697,10 @@ public final class AntarchySettings { return experimentalSettingsPopupDisabled; } + public static boolean unlockAllArchives() { + return unlockAllArchives; + } + public static boolean sizeChangingRaysEnabled() { return sizeChangingRaysEnabled; } @@ -1315,6 +1320,10 @@ public final class AntarchySettings { experimentalSettingsPopupDisabled = value; } + public static void setUnlockAllArchives(boolean value) { + unlockAllArchives = value; + } + public static void setSizeChangingRaysEnabled(boolean value) { sizeChangingRaysEnabled = value; } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/ComputerBlockEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/ComputerBlockEntity.java index f4dc81f8..9adee0ce 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/ComputerBlockEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/block/entity/ComputerBlockEntity.java @@ -4,6 +4,7 @@ import com.craisinlord.antarchy.content.AntarchyObjects; import com.craisinlord.antarchy.content.block.ComputerBlock; import com.craisinlord.antarchy.content.computer.ComputerFileSystem; import com.craisinlord.antarchy.content.computer.ComputerDesktopState; +import com.craisinlord.antarchy.content.guide.ComputerGuideData; import net.minecraft.core.BlockPos; import net.minecraft.core.HolderLookup; import net.minecraft.nbt.CompoundTag; @@ -92,6 +93,13 @@ public final class ComputerBlockEntity extends BlockEntity implements GeoBlockEn return true; } this.physicalDiskIds.add(diskId); + ComputerGuideData.Disk disk = ComputerGuideData.disk(diskId); + if (disk != null && !disk.wallpaper().isBlank()) { + try { + desktopState.unlockWallpaper(ResourceLocation.parse(disk.wallpaper())); + } catch (RuntimeException ignored) { + } + } stack.shrink(1); activate(); setChanged(); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/ComputerFileSystemClientState.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/ComputerFileSystemClientState.java index 73e184ef..00e6a51f 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/ComputerFileSystemClientState.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/ComputerFileSystemClientState.java @@ -28,15 +28,24 @@ public final class ComputerFileSystemClientState { State state = STATES.computeIfAbsent(payload.pos(), ignored -> new State()); state.success = payload.result() == ComputerAccessResultPayload.SUCCESS; state.error = envelope[1]; - if (action == ComputerAccessPayload.FILE_LIST && state.success) { + if (state.success) state.error = ""; + if (action == ComputerAccessPayload.FILE_LIST) { state.files = new ArrayList<>(); - if (!envelope[2].isEmpty()) state.files.addAll(List.of(envelope[2].split("\\n"))); - } else if (action == ComputerAccessPayload.FILE_OPEN && state.success) { - String[] file = envelope[2].split("\u0000", 2); - if (file.length == 2) { - state.openedPath = file[0]; - state.openedContents = file[1]; + if (state.success && !envelope[2].isEmpty()) state.files.addAll(List.of(envelope[2].split("\\n"))); + } else if (action == ComputerAccessPayload.FILE_OPEN) { + if (state.success) { + String[] file = envelope[2].split("\u0000", 2); + if (file.length == 2) { + state.openedPath = file[0]; + state.openedContents = file[1]; + } + } else { + state.openedPath = ""; + state.openedContents = ""; } + } else if ((action == ComputerAccessPayload.FILE_DELETE || action == ComputerAccessPayload.FILE_MOVE) && state.success) { + state.openedPath = ""; + state.openedContents = ""; } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/RoyalBossRenderer.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/RoyalBossRenderer.java index af43c516..2ba7b663 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/RoyalBossRenderer.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/RoyalBossRenderer.java @@ -309,9 +309,9 @@ public class RoyalBossRenderer extends GeoEntityRenderer { private static void drawModelPlane(VertexConsumer vertices, org.joml.Matrix4f pose, int x, int y, int z, int width, int height, int u, int v) { emitQuadUv(vertices, pose, - new Vec3(x, y, z), new Vec3(x + width, y, z), - new Vec3(x + width, y + height, z), new Vec3(x, y + height, z), - u / 128.0F, v / 128.0F, (u + width) / 128.0F, (v + height) / 128.0F); + new Vec3(x + width, y, z), new Vec3(x, y, z), + new Vec3(x, y + height, z), new Vec3(x + width, y + height, z), + (u + width) / 128.0F, v / 128.0F, u / 128.0F, (v + height) / 128.0F); } private static void emitQuadUv(VertexConsumer vertices, org.joml.Matrix4f pose, diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/screen/ComputerScreen.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/screen/ComputerScreen.java index 0b627eb5..8d38bd8b 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/screen/ComputerScreen.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/screen/ComputerScreen.java @@ -31,6 +31,10 @@ import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.client.gui.screens.inventory.InventoryScreen; +import net.minecraft.world.item.ItemStack; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -108,6 +112,19 @@ public final class ComputerScreen extends Screen { } } } + AntmailResultPayload mailResult = AntmailClientState.get(position); + if (session.antmailRegistrationPending && mailResult != null) { + String expectedAddress = session.antmailUsername + "@antmail.com"; + if (mailResult.address().equalsIgnoreCase(expectedAddress)) { + session.antmailStatus = "ADDRESS REGISTERED"; + session.antmailRegistrationPending = false; + AntmailNetworking.requestState(position); + } else if (mailResult.detail().startsWith("registration_failed:")) { + String reason = mailResult.detail().substring("registration_failed:".length()).trim(); + session.antmailStatus = "ADDRESS REGISTRATION FAILED // " + (reason.isBlank() ? "SERVER REJECTED" : reason.toUpperCase()); + session.antmailRegistrationPending = false; + } + } if (!loggedIn && accessResult != null && accessResult.authenticated()) { loggedIn = true; session.authenticated = true; @@ -134,6 +151,24 @@ public final class ComputerScreen extends Screen { } } } + if (currentResult != null && currentResult.data().startsWith("15\u0000") && !currentResult.data().equals(session.terminalResponse)) { + session.terminalResponse = currentResult.data(); + try { + var terminal = AntmailWire.decodeTag(currentResult.data().substring(3)); + session.terminalDirectory = terminal.getString("Directory"); + if (terminal.getBoolean("Clear")) session.terminalOutput.clear(); + var lines = terminal.getList("Lines", 8); + for (int index = 0; index < lines.size(); index++) session.terminalOutput.add(lines.getString(index)); + String openPath = terminal.getString("Open"); + if (!openPath.isBlank()) { + ComputerNetworking.openFile(position, openPath); + open(openPath.endsWith(".antpaint") ? "PAINT" : "TEXT"); + } + while (session.terminalOutput.size() > 40) session.terminalOutput.remove(0); + } catch (RuntimeException ignored) { + session.terminalOutput.add("ERROR: CORRUPTED SERVER RESPONSE"); + } + } antFarm.setGameInstalled(physicalDisks().stream().anyMatch(id -> id.getPath().equals("ant_farm"))); if (activeWindow != null && activeWindow.type.equals("GAMES") && activeWindow.gameOpen) antFarm.tick(); } @@ -192,9 +227,11 @@ public final class ComputerScreen extends Screen { private void renderDesktop(GuiGraphics g, int l, int t, int mouseX, int mouseY) { session.lastUse = System.currentTimeMillis(); - if (session.wallpaperId.endsWith("grid_ant")) { - for (int py = t + 36; py < t + HEIGHT - 8; py += 18) for (int px = l + 8; px < l + WIDTH - 8; px += 36) g.fill(px, py, px + 2, py + 2, 0xFF173817); - } + session.mouseX = mouseX; + session.mouseY = mouseY; + session.archiveMouseX = mouseX; + session.archiveMouseY = mouseY; + renderWallpaper(g, l, t); g.fill(l + 9, t + 9, l + WIDTH - 9, t + 30, BLACK); g.drawString(font, Component.literal("ANTINTOSH OS // v" + Antarchy.MOD_VERSION), l + 18, t + 15, GREEN, false); long dayTime = Minecraft.getInstance().level == null ? 0L : Minecraft.getInstance().level.getDayTime(); @@ -222,19 +259,133 @@ public final class ComputerScreen extends Screen { for (Window window : session.windows) if (!window.minimized) renderWindow(g, window, l, t, mouseX, mouseY); } + private void renderWallpaper(GuiGraphics g, int l, int t) { + int left = l + 9; + int top = t + 34; + int right = l + WIDTH - 9; + int bottom = t + HEIGHT - 8; + String wallpaperId = ComputerDesktopState.DEFAULT_WALLPAPER.toString(); + int hash = wallpaperId.hashCode(); + int green = 24 + Math.floorMod(hash, 40); + int blue = 12 + Math.floorMod(hash >>> 8, 24); + int base = 0xFF000000 | (green / 2 << 16) | (green << 8) | blue; + g.fill(left, top, right, bottom, base); + if (wallpaperId.endsWith("grid_ant")) { + for (int py = top + 2; py < bottom; py += 18) { + for (int px = left + 2; px < right; px += 36) { + g.fill(px, py, px + 2, py + 2, 0xFF173817); + } + } + return; + } + int accent = 0xFF000000 | (Math.min(120, green + 34) << 8) | Math.min(80, blue + 34); + int spacing = 18 + Math.floorMod(hash, 13); + int offset = Math.floorMod(hash >>> 16, spacing); + for (int y = top - spacing; y < bottom + spacing; y += spacing) { + int x = left - spacing + offset; + while (x < right) { + int size = 3 + Math.floorMod(x + y + hash, 7); + g.fill(x, y, Math.min(right, x + size), Math.min(bottom, y + 2), accent); + x += spacing * 2; + } + offset = spacing - offset; + } + } + + private String wallpaperName() { + int separator = session.wallpaperId.indexOf(':'); + String name = separator >= 0 ? session.wallpaperId.substring(separator + 1) : session.wallpaperId; + return name.toUpperCase(); + } + private void icon(GuiGraphics g, String type, int x, int y) { + if (type.equals("ARCHIVE") || type.equals("DISK")) { + g.fill(x - 11, y - 2, x + 11, y + 19, BLACK); + g.fill(x - 8, y + 1, x + 8, y + 16, GREEN); + g.fill(x - 5, y + 3, x + 5, y + 8, BLACK); + g.fill(x - 4, y + 12, x + 4, y + 16, BLACK); + return; + } + if (type.equals("FILES")) { + g.fill(x - 12, y + 2, x + 11, y + 19, BLACK); + g.fill(x - 9, y, x + 1, y + 4, BLACK); + g.fill(x - 8, y + 3, x + 9, y + 16, GREEN); + g.fill(x - 9, y + 8, x + 9, y + 16, GREEN); + g.fill(x - 6, y + 2, x - 1, y + 4, GREEN); + return; + } + if (type.equals("SETTINGS")) { + g.fill(x - 5, y - 3, x + 5, y + 20, BLACK); + g.fill(x - 12, y + 5, x + 12, y + 13, BLACK); + g.fill(x - 9, y, x + 9, y + 18, BLACK); + g.fill(x - 2, y, x + 2, y + 17, GREEN); + g.fill(x - 9, y + 7, x + 9, y + 11, GREEN); + g.fill(x - 3, y + 5, x + 3, y + 13, BLACK); + return; + } + if (type.equals("TERMINAL")) { + g.fill(x - 12, y - 1, x + 12, y + 18, BLACK); + g.fill(x - 9, y + 2, x + 9, y + 15, GREEN); + g.fill(x - 6, y + 6, x - 2, y + 9, BLACK); + g.fill(x - 2, y + 9, x + 4, y + 12, BLACK); + return; + } + if (type.equals("TEXT")) { + g.fill(x - 9, y - 2, x + 10, y + 20, BLACK); + g.fill(x - 6, y + 1, x + 7, y + 17, GREEN); + g.fill(x + 5, y + 1, x + 7, y + 4, BLACK); + g.fill(x - 3, y + 5, x + 5, y + 7, BLACK); + g.fill(x - 3, y + 9, x + 5, y + 11, BLACK); + g.fill(x - 3, y + 13, x + 3, y + 15, BLACK); + return; + } + if (type.equals("PAINT")) { + // Palette with a separate brush, using a stepped silhouette to keep the icon readable at desktop scale. + g.fill(x - 10, y + 2, x + 8, y + 19, BLACK); + g.fill(x - 7, y + 4, x + 6, y + 16, GREEN); + g.fill(x - 4, y + 6, x - 1, y + 9, BLACK); + g.fill(x + 1, y + 5, x + 4, y + 8, BLACK); + g.fill(x + 2, y + 11, x + 5, y + 14, BLACK); + g.fill(x + 8, y - 3, x + 11, y + 10, BLACK); + g.fill(x + 5, y + 6, x + 9, y + 9, BLACK); + g.fill(x + 8, y - 1, x + 10, y + 7, PALE_GREEN); + g.fill(x + 5, y + 7, x + 8, y + 10, PALE_GREEN); + return; + } + if (type.equals("ANTMAIL")) { + g.fill(x - 12, y + 2, x + 12, y + 17, BLACK); + g.fill(x - 9, y + 5, x + 9, y + 14, GREEN); + // Sealed envelope: the stepped black V is the closed flap. + g.fill(x - 8, y + 5, x - 5, y + 7, BLACK); + g.fill(x - 6, y + 7, x - 3, y + 9, BLACK); + g.fill(x - 4, y + 9, x - 1, y + 11, BLACK); + g.fill(x - 1, y + 11, x + 2, y + 13, BLACK); + g.fill(x + 1, y + 9, x + 4, y + 11, BLACK); + g.fill(x + 3, y + 7, x + 6, y + 9, BLACK); + g.fill(x + 5, y + 5, x + 8, y + 7, BLACK); + return; + } + if (type.equals("GAMES")) { + g.fill(x - 12, y + 6, x + 12, y + 15, BLACK); + g.fill(x - 9, y + 3, x + 9, y + 18, BLACK); + g.fill(x - 8, y + 6, x + 8, y + 15, GREEN); + g.fill(x - 5, y + 8, x + 1, y + 10, BLACK); + g.fill(x - 3, y + 6, x - 1, y + 12, BLACK); + g.fill(x + 4, y + 7, x + 6, y + 9, BLACK); + g.fill(x + 7, y + 10, x + 9, y + 12, BLACK); + return; + } + if (type.equals("TRASH")) { + g.fill(x - 9, y + 2, x + 9, y + 5, BLACK); + g.fill(x - 8, y + 5, x + 8, y + 19, BLACK); + g.fill(x - 5, y + 7, x + 5, y + 16, GREEN); + g.fill(x - 6, y + 3, x + 6, y + 4, GREEN); + g.fill(x - 5, y - 1, x + 5, y + 2, BLACK); + g.fill(x - 3, y, x + 3, y + 1, GREEN); + return; + } g.fill(x - 10, y, x + 10, y + 18, BLACK); g.fill(x - 8, y + 2, x + 8, y + 16, GREEN); - if (type.equals("SETTINGS")) { - g.fill(x - 12, y + 7, x + 12, y + 11, BLACK); - g.fill(x - 2, y - 2, x + 2, y + 20, BLACK); - } else if (type.equals("TRASH")) { - g.fill(x - 12, y - 3, x + 12, y, GREEN); - g.fill(x - 7, y + 4, x + 7, y + 18, BLACK); - } else if (type.equals("TERMINAL")) { - g.fill(x - 5, y + 7, x - 1, y + 11, BLACK); - g.fill(x - 1, y + 11, x + 6, y + 14, BLACK); - } } private void renderWindow(GuiGraphics g, Window window, int l, int t, int mouseX, int mouseY) { @@ -254,12 +405,12 @@ public final class ComputerScreen extends Screen { int cx = x + 8; int cy = y + 28; if (window.type.equals("ARCHIVE")) renderArchive(g, cx, cy, w - 16, h - 34); - else if (window.type.equals("FILES")) renderFileExplorer(g, cx, cy, w, h); + else if (window.type.equals("FILES")) renderFileExplorer(g, cx, cy, w - 16, h - 34); else if (window.type.equals("SETTINGS")) renderSettings(g, cx, cy, h - 34); - else if (window.type.equals("TERMINAL")) renderTerminal(g, cx, cy, h); - else if (window.type.equals("TEXT")) renderTextEditor(g, cx, cy, w, h); - else if (window.type.equals("PAINT")) renderPaint(g, cx, cy, w, h); - else if (window.type.equals("ANTMAIL")) renderAntmail(g, cx, cy, h); + else if (window.type.equals("TERMINAL")) renderTerminal(g, cx, cy, h - 34); + else if (window.type.equals("TEXT")) renderTextEditor(g, cx, cy, w - 16, h - 34); + else if (window.type.equals("PAINT")) renderPaint(g, cx, cy, w - 16, h - 34); + else if (window.type.equals("ANTMAIL")) renderAntmail(g, cx, cy, h - 34); else if (window.type.equals("GAMES")) renderGames(g, cx, cy); else renderTrash(g, cx, cy); } @@ -305,17 +456,51 @@ public final class ComputerScreen extends Screen { return; } int row = y + 18; - int visible = Math.max(1, (h - 34) / 25); - for (int i = 0; i < entries.size() && i < visible; i++) { + int visible = Math.max(1, (h - 34) / 62); + int start = Math.max(0, Math.min(session.archiveScroll, Math.max(0, entries.size() - visible))); + for (int i = start; i < entries.size() && i < start + visible; i++) { ComputerGuideData.Entry entry = entries.get(i); - int rowTop = row + i * 25; - g.drawString(font, Component.literal(String.format("FILE_%02d", i + 1)), x + 5, rowTop, PALE_GREEN, false); - String title = trimToWidth(Component.translatable(entry.titleKey()).getString(), w - 64); - g.drawString(font, Component.literal(title), x + 57, rowTop, GREEN, false); - g.drawString(font, Component.literal(entry.type().toUpperCase() + " // " + entry.category().toUpperCase()), x + 57, rowTop + 11, PALE_GREEN, false); + int rowTop = row + (i - start) * 62; + boolean hover = inside(x, rowTop, w - 8, 56, session.archiveMouseX, session.archiveMouseY); + if (hover) g.fill(x, rowTop, x + w - 8, rowTop + 56, 0xFF173817); + g.fill(x + 3, rowTop + 3, x + 51, rowTop + 51, BLACK); + renderThumbnail(g, entry, x + 27, rowTop + 27, hover); + String title = trimToWidth(Component.translatable(entry.titleKey()).getString(), w - 78); + g.drawString(font, Component.literal(title), x + 58, rowTop + 9, GREEN, false); + if (!entry.subtitleKey().isBlank()) g.drawString(font, Component.literal(trimToWidth(Component.translatable(entry.subtitleKey()).getString(), w - 78)), x + 58, rowTop + 23, PALE_GREEN, false); + g.drawString(font, Component.literal(entry.type().toUpperCase() + " // " + entry.category().toUpperCase()), x + 58, rowTop + 39, PALE_GREEN, false); } - if (entries.size() > visible) g.drawString(font, Component.literal("SCROLLING NOT AVAILABLE // FIRST FILES SHOWN"), x, y + h - 12, PALE_GREEN, false); - else g.drawString(font, Component.literal("[ CLICK FILE TO OPEN ]"), x, y + h - 12, PALE_GREEN, false); + if (entries.size() > visible) g.drawString(font, Component.literal("SCROLL " + (start + 1) + "-" + Math.min(entries.size(), start + visible) + " / " + entries.size()), x, y + h - 12, PALE_GREEN, false); + else g.drawString(font, Component.literal("[ CLICK ENTRY TO OPEN ]"), x, y + h - 12, PALE_GREEN, false); + } + + private void renderThumbnail(GuiGraphics g, ComputerGuideData.Entry entry, int centerX, int centerY, boolean hover) { + int background = hover ? 0xFF173817 : 0xFF102010; + g.fill(centerX - 18, centerY - 18, centerX + 18, centerY + 18, background); + if (!entry.itemId().isBlank()) { + try { + ResourceLocation itemId = ResourceLocation.parse(entry.itemId()); + var item = BuiltInRegistries.ITEM.getOptional(itemId).orElse(null); + if (item != null && !item.equals(net.minecraft.world.item.Items.AIR)) { + g.renderItem(new ItemStack(item), centerX - 8, centerY - 8); + return; + } + } catch (RuntimeException ignored) { + } + } + if (!entry.entityId().isBlank() && Minecraft.getInstance().level != null) { + try { + ResourceLocation entityId = ResourceLocation.parse(entry.entityId()); + var entityType = BuiltInRegistries.ENTITY_TYPE.getOptional(entityId).orElse(null); + var entity = entityType == null ? null : entityType.create(Minecraft.getInstance().level); + if (entity instanceof LivingEntity living && !living.isRemoved()) { + InventoryScreen.renderEntityInInventoryFollowsMouse(g, centerX - 18, centerY - 18, centerX + 18, centerY + 18, 18, 0.15F, centerX, centerY, living); + return; + } + } catch (RuntimeException ignored) { + } + } + g.drawString(font, Component.literal("ANT"), centerX - 12, centerY - 4, hover ? GREEN : PALE_GREEN, false); } private void renderFileExplorer(GuiGraphics g, int x, int y, int w, int h) { @@ -327,12 +512,19 @@ public final class ComputerScreen extends Screen { for (String file : files) { String[] fields = file.split("\\t", 3); if (fields.length < 3 || !isDirectChild(fields[1], session.fileExplorerDirectory) || line > y + h - 20) continue; - g.drawString(font, Component.literal(trimToWidth(fields[0], 36)), x, line, PALE_GREEN, false); - g.drawString(font, Component.literal(trimToWidth(fields[1], 125)), x + 42, line, GREEN, false); - g.drawString(font, Component.literal(fields[2]), x + w - 28, line, PALE_GREEN, false); + boolean selected = fields[1].equals(session.fileExplorerSelected); + if (selected) g.fill(x - 3, line - 2, x + w - 3, line + 11, 0xFF173817); + g.drawString(font, Component.literal(fileIcon(fields[0]) + " " + trimToWidth(fields[0], 30)), x, line, PALE_GREEN, false); + g.drawString(font, Component.literal(trimToWidth(fields[1], 112)), x + 42, line, GREEN, false); + g.drawString(font, Component.literal(trimToWidth(fields[2], 28)), x + w - 28, line, PALE_GREEN, false); line += 14; } if (line == y + 34) g.drawString(font, Component.literal("NO FILES // OPEN TERMINAL TO CREATE ONE"), x, y + 38, PALE_GREEN, false); + else g.drawString(font, Component.literal(session.fileExplorerRenaming ? "RENAME: " + session.fileExplorerRename + "_" : "[ DELETE ] [ F2 RENAME ]"), x, y + h - 16, PALE_GREEN, false); + } + + private String fileIcon(String type) { + return type.equals("DIRECTORY") ? "[D]" : type.equals("TEXT") ? "[T]" : "[I]"; } private boolean isDirectChild(String path, String directory) { @@ -360,9 +552,8 @@ public final class ComputerScreen extends Screen { g.drawString(font, Component.literal("SYSTEM STATUS"), x, y, GREEN, false); g.drawString(font, Component.literal("CPU " + (System.currentTimeMillis() / 100 % 87 + 12) + "%"), x, y + 18, PALE_GREEN, false); g.drawString(font, Component.literal("MEM " + (System.currentTimeMillis() / 250 % 42 + 31) + "%"), x, y + 32, PALE_GREEN, false); - g.drawString(font, Component.literal("WALLPAPER " + session.wallpaperId.substring(session.wallpaperId.indexOf(':') + 1).toUpperCase()), x, y + 46, PALE_GREEN, false); - g.drawString(font, Component.literal("[ CLICK TO CYCLE ]"), x, y + 60, GREEN, false); - g.drawString(font, Component.literal("PASSWORD CHANGE IN FULL BUILD"), x, y + 70, GREEN, false); + g.drawString(font, Component.literal("WALLPAPER DEFAULT // LOCKED"), x, y + 46, PALE_GREEN, false); + g.drawString(font, Component.literal("PASSWORD CHANGE IN FULL BUILD"), x, y + 60, GREEN, false); g.drawString(font, Component.literal("PHYSICAL DISKS"), x, y + 91, GREEN, false); List disks = physicalDisks(); if (disks.isEmpty()) { @@ -379,6 +570,21 @@ public final class ComputerScreen extends Screen { g.drawString(font, Component.literal("LOG OUT"), x, y + h - 17, GREEN, false); } + private void drawWallpaperPreview(GuiGraphics g, int x, int y, int w, int h, String id) { + int hash = id.hashCode(); + int green = 24 + Math.floorMod(hash, 40); + int blue = 12 + Math.floorMod(hash >>> 8, 24); + int base = 0xFF000000 | (green / 2 << 16) | (green << 8) | blue; + g.fill(x - 1, y - 1, x + w + 1, y + h + 1, GREEN); + g.fill(x, y, x + w, y + h, base); + int accent = 0xFF000000 | (Math.min(120, green + 34) << 8) | Math.min(80, blue + 34); + for (int py = y + 4; py < y + h; py += 9) { + for (int px = x + 4; px < x + w; px += 12) { + if (Math.floorMod(px + py + hash, 3) == 0) g.fill(px, py, Math.min(x + w, px + 3), Math.min(y + h, py + 2), accent); + } + } + } + private void renderTerminal(GuiGraphics g, int x, int y, int h) { var fileState = ComputerFileSystemClientState.get(position); g.drawString(font, Component.literal("ANTINTOSH TERMINAL [READY]"), x, y, GREEN, false); @@ -387,8 +593,8 @@ public final class ComputerScreen extends Screen { line = wrap(g, output, x, line, 208, PALE_GREEN) + 1; if (line > y + h - 32) break; } - if (!fileState.error().isEmpty()) g.drawString(font, Component.literal("RESPONSE: " + fileState.error()), x, y + 16, PALE_GREEN, false); - g.drawString(font, Component.literal(session.terminalDirectory + "> " + session.terminalInput + "_"), x, y + h - 48, GREEN, false); + if (!fileState.error().isEmpty()) g.drawString(font, Component.literal("RESPONSE: " + fileState.error()), x, y + h - 40, PALE_GREEN, false); + g.drawString(font, Component.literal(session.terminalDirectory + "> " + session.terminalInput + "_"), x, y + h - 26, GREEN, false); } private void renderPaint(GuiGraphics g, int x, int y, int w, int h) { @@ -402,14 +608,27 @@ public final class ComputerScreen extends Screen { } } g.drawString(font, Component.literal("ANTPAINT // " + session.paintName + (session.paintDirty ? " *" : "")), x, y, GREEN, false); - g.drawString(font, Component.literal("NEW PENCIL ERASER FILL CLEAR UNDO REDO SAVE"), x, y + 14, PALE_GREEN, false); - int size = Math.min((w - 16) / AntPaintCanvas.WIDTH, (h - 54) / AntPaintCanvas.HEIGHT); + String controls = "NEW PENCIL ERASER FILL CLEAR UNDO REDO SAVE"; + g.drawString(font, Component.literal(trimToWidth(controls, w)), x, y + 14, PALE_GREEN, false); + int size = Math.max(1, Math.min((w - 16) / AntPaintCanvas.WIDTH, (h - 54) / AntPaintCanvas.HEIGHT)); int cx = x + (w - size * AntPaintCanvas.WIDTH) / 2; - int cy = y + 30; + int cy = y + 30 + Math.max(0, (h - 54 - size * AntPaintCanvas.HEIGHT) / 2); g.fill(cx - 1, cy - 1, cx + size * AntPaintCanvas.WIDTH + 1, cy + size * AntPaintCanvas.HEIGHT + 1, BLACK); g.fill(cx, cy, cx + size * AntPaintCanvas.WIDTH, cy + size * AntPaintCanvas.HEIGHT, GREEN); for (int py = 0; py < AntPaintCanvas.HEIGHT; py++) for (int px = 0; px < AntPaintCanvas.WIDTH; px++) if (!session.paintCanvas.get(px, py)) g.fill(cx + px * size, cy + py * size, cx + (px + 1) * size, cy + (py + 1) * size, BLACK); - g.drawString(font, Component.literal("TOOL " + session.paintTool.name() + " " + (session.paintDirty ? "MODIFIED" : "SAVED")), x, y + h - 16, PALE_GREEN, false); + int hoverX = (session.mouseX - cx) / size; + int hoverY = (session.mouseY - cy) / size; + if (session.mouseX >= cx && session.mouseY >= cy && hoverX >= 0 && hoverX < AntPaintCanvas.WIDTH && hoverY >= 0 && hoverY < AntPaintCanvas.HEIGHT) { + int pixelX = cx + hoverX * size; + int pixelY = cy + hoverY * size; + g.fill(pixelX, pixelY, pixelX + size, pixelY + 1, PALE_GREEN); + g.fill(pixelX, pixelY + size - 1, pixelX + size, pixelY + size, PALE_GREEN); + g.fill(pixelX, pixelY, pixelX + 1, pixelY + size, PALE_GREEN); + g.fill(pixelX + size - 1, pixelY, pixelX + size, pixelY + size, PALE_GREEN); + } + String paintStatus = "TOOL " + session.paintTool.name() + " " + (session.paintDirty ? "MODIFIED" : "SAVED"); + if (session.mouseX >= cx && session.mouseY >= cy && hoverX >= 0 && hoverX < AntPaintCanvas.WIDTH && hoverY >= 0 && hoverY < AntPaintCanvas.HEIGHT) paintStatus += " PIXEL " + hoverX + "," + hoverY; + g.drawString(font, Component.literal(trimToWidth(paintStatus, w)), x, y + h - 16, PALE_GREEN, false); } private void renderTextEditor(GuiGraphics g, int x, int y, int w, int h) { @@ -418,7 +637,7 @@ public final class ComputerScreen extends Screen { session.textPath = fileState.openedPath(); session.textContent = fileState.openedContents(); } - g.drawString(font, Component.literal("ANTTEXT // " + session.textPath + (session.textDirty ? " *" : "")), x, y, GREEN, false); + g.drawString(font, Component.literal(session.textRenaming ? "RENAME // " + session.textRename + "_" : "ANTTEXT // " + session.textPath + (session.textDirty ? " *" : "")), x, y, GREEN, false); g.drawString(font, Component.literal("NEW OPEN WRITE SAVE"), x, y + 18, PALE_GREEN, false); g.fill(x + 4, y + 36, x + w - 4, y + h - 4, BLACK); if (session.textListing) { @@ -434,7 +653,7 @@ public final class ComputerScreen extends Screen { } else { wrap(g, session.textContent.isEmpty() ? "TYPE HERE..." : session.textContent, x + 10, y + 46, w - 28, GREEN); } - g.drawString(font, Component.literal("[ CTRL+S ] SAVE [ ESC ] CLOSE"), x, y + h - 50, PALE_GREEN, false); + g.drawString(font, Component.literal("[ CTRL+S ] SAVE [ F2 ] RENAME [ ESC ] CLOSE"), x, y + h - 50, PALE_GREEN, false); } private void renderAntmail(GuiGraphics g, int x, int y, int h) { @@ -446,40 +665,53 @@ public final class ComputerScreen extends Screen { g.drawString(font, Component.literal("USERNAME // 3-16 CHARACTERS"), x, y + 40, GREEN, false); g.fill(x, y + 56, x + 206, y + 79, BLACK); box(g, x, y + 56, x + 206, y + 79, GREEN); - g.drawString(font, Component.literal(session.antmailUsername + "@ANTMAIL.COM"), x + 6, y + 63, GREEN, false); + g.drawString(font, Component.literal(trimToWidth(session.antmailUsername + "@antmail.com", 194)), x + 6, y + 63, GREEN, false); g.drawString(font, Component.literal("[ ENTER ] REGISTER ADDRESS"), x, y + 88, GREEN, false); } else { - g.drawString(font, Component.literal(result.address()), x, y + 20, PALE_GREEN, false); - g.drawString(font, Component.literal("[ INBOX ] [ SENT ] [ COMPOSE ]"), x, y + 40, GREEN, false); + g.drawString(font, Component.literal(trimToWidth(result.address(), 208)), x, y + 20, PALE_GREEN, false); + g.drawString(font, Component.literal("[ INBOX ] [ SENT ]"), x, y + 40, GREEN, false); + box(g, x + 122, y + 30, x + 214, y + 53, GREEN); + g.drawString(font, Component.literal("CREATE COMPOSE"), x + 127, y + 38, GREEN, false); if (session.antmailMode.equals("compose")) { g.drawString(font, Component.literal("TO"), x, y + 62, GREEN, false); - g.drawString(font, Component.literal(session.antmailRecipient), x + 28, y + 62, PALE_GREEN, false); + g.drawString(font, Component.literal(trimToWidth(session.antmailRecipient, 180)), x + 28, y + 62, PALE_GREEN, false); g.drawString(font, Component.literal("SUBJECT"), x, y + 78, GREEN, false); - g.drawString(font, Component.literal(session.antmailSubject), x + 52, y + 78, PALE_GREEN, false); + g.drawString(font, Component.literal(trimToWidth(session.antmailSubject, 156)), x + 52, y + 78, PALE_GREEN, false); g.drawString(font, Component.literal("BODY"), x, y + 94, GREEN, false); wrap(g, session.antmailBody.isEmpty() ? "TYPE MESSAGE..." : session.antmailBody, x, y + 110, 208, PALE_GREEN); - g.drawString(font, Component.literal("[ " + (session.antmailAttachText ? "X" : " ") + " ] TEXT FILE [ " + (session.antmailAttachPaint ? "X" : " ") + " ] PAINTING"), x, y + 142, PALE_GREEN, false); + g.drawString(font, Component.literal(trimToWidth("[ " + (session.antmailAttachText ? "X" : " ") + " ] TEXT FILE [ " + (session.antmailAttachPaint ? "X" : " ") + " ] PAINTING", 208)), x, y + 142, PALE_GREEN, false); g.drawString(font, Component.literal("[ ENTER ] SEND MESSAGE"), x, y + h - 30, GREEN, false); } else if (session.antmailMode.equals("message")) { AntmailMessage message = selectedAntmailMessage(result); if (message != null) { - g.drawString(font, Component.literal("< BACK // INBOX"), x, y + 40, GREEN, false); - g.drawString(font, Component.literal(message.sender().fullAddress()), x, y + 62, PALE_GREEN, false); - g.drawString(font, Component.literal(message.subject()), x, y + 78, GREEN, false); - wrap(g, message.body(), x, y + 98, 208, PALE_GREEN); + g.drawString(font, Component.literal("< BACK // " + (session.antmailSent ? "SENT" : "INBOX")), x, y + 40, GREEN, false); + g.drawString(font, Component.literal(trimToWidth(session.antmailSent ? message.recipient().fullAddress() : message.sender().fullAddress(), 208)), x, y + 62, PALE_GREEN, false); + g.drawString(font, Component.literal(trimToWidth(message.subject(), 208)), x, y + 78, GREEN, false); + int messageLine = wrap(g, message.body(), x, y + 98, 208, PALE_GREEN) + 4; + for (AntmailAttachment attachment : message.attachments()) { + if (messageLine > y + h - 38) break; + messageLine = wrap(g, "ATTACHMENT // " + attachment.fileName(), x, messageLine, 208, GREEN) + 2; + } } } else { - g.drawString(font, Component.literal("INBOX // " + mailboxMessages(result, false).size()), x, y + 62, GREEN, false); + List messages = mailboxMessages(result, session.antmailSent); + g.drawString(font, Component.literal((session.antmailSent ? "SENT // " : "INBOX // ") + messages.size()), x, y + 62, GREEN, false); int line = y + 80; - for (AntmailMessage message : mailboxMessages(result, false)) { + for (AntmailMessage message : messages) { if (line > y + h - 38) break; g.drawString(font, Component.literal((message.read() ? " " : "* ") + trimToWidth(message.subject(), 28)), x, line, message.read() ? PALE_GREEN : GREEN, false); line += 14; } } } - if (!session.antmailStatus.isEmpty()) g.drawString(font, Component.literal(session.antmailStatus), x, y + h - 30, PALE_GREEN, false); - g.drawString(font, Component.literal(result == null ? "WAITING FOR SERVER" : result.status() == 0 ? "DELIVERY READY" : "SERVER RESPONSE: " + result.detail()), x, y + h - 16, GREEN, false); + String displayStatus = result != null && !result.messageId().isBlank() ? antmailStatus(result) : session.antmailStatus; + if (!displayStatus.isEmpty()) drawBottomWrapped(g, displayStatus, x, y, h, 208, PALE_GREEN); + } + + private String antmailStatus(AntmailResultPayload result) { + if (result.status() == 0) return "DELIVERED"; + if (result.status() == 1) return "QUEUED"; + return "DELIVERY FAILED // " + (result.detail().isBlank() ? "SERVER REJECTED" : result.detail().toUpperCase()); } private AntmailMailbox mailbox(AntmailResultPayload result) { @@ -505,9 +737,11 @@ public final class ComputerScreen extends Screen { private void renderGames(GuiGraphics g, int x, int y) { g.drawString(font, Component.literal("INSTALLED GAMES"), x, y, GREEN, false); if (activeWindow != null && activeWindow.gameOpen) { antFarm.render(g, font, x, y + 16, 214, 155); return; } - g.drawString(font, Component.literal(antFarm.isGameInstalled() ? "ANT FARM [ ENTER ]" : "ANT FARM [NOT INSTALLED]"), x, y + 22, PALE_GREEN, false); - g.drawString(font, Component.literal("GLIMMER MATCH"), x, y + 38, PALE_GREEN, false); - g.drawString(font, Component.literal("HFCS FACTORY"), x, y + 54, PALE_GREEN, false); + if (antFarm.isGameInstalled()) g.drawString(font, Component.literal("ANT FARM [ ENTER ]"), x, y + 22, PALE_GREEN, false); + else { + g.drawString(font, Component.literal("NO GAMES INSTALLED"), x, y + 22, PALE_GREEN, false); + g.drawString(font, Component.literal("FIND GAME DISKS TO INSTALL PROGRAMS"), x, y + 40, PALE_GREEN, false); + } } private void renderTrash(GuiGraphics g, int x, int y) { @@ -522,6 +756,15 @@ public final class ComputerScreen extends Screen { return y; } + private void drawBottomWrapped(GuiGraphics g, String text, int x, int y, int h, int width, int color) { + List lines = font.split(Component.literal(text), width); + int lineY = y + h - 16 - Math.max(0, lines.size() - 1) * 11; + for (var line : lines) { + g.drawString(font, line, x, lineY, color, false); + lineY += 11; + } + } + private void box(GuiGraphics g, int x1, int y1, int x2, int y2, int color) { g.fill(x1, y1, x2, y1 + 2, color); g.fill(x1, y2 - 2, x2, y2, color); @@ -553,6 +796,14 @@ public final class ComputerScreen extends Screen { int h = window.maximized ? HEIGHT - 48 : window.height; if (inside(x, y + 20, w, h - 20, mouseX, mouseY)) { activeWindow = window; + session.windows.remove(i); + session.windows.add(window); + if (window.type.equals("SETTINGS") && inside(x + 8, y + 105, 205, 51, mouseX, mouseY)) { + List disks = physicalDisks(); + int index = ((int) mouseY - (y + 105)) / 17; + if (index >= 0 && index < disks.size() && index < 3) selectedDisk = disks.get(index); + return true; + } if (window.type.equals("ARCHIVE")) { List entries = ComputerGuideData.entriesFor(computer() == null ? List.of() : computer().diskIds()); int archiveX = x + 8; @@ -560,9 +811,11 @@ public final class ComputerScreen extends Screen { if (session.archiveEntryId != null) { if (inside(archiveX, archiveY, w - 16, 20, mouseX, mouseY)) session.archiveEntryId = null; } else { - int visible = Math.max(1, (h - 34) / 25); - int index = ((int) mouseY - (archiveY + 16)) / 25; - if (index >= 0 && index < entries.size() && index < visible && inside(archiveX, archiveY + 16 + index * 25, w - 16, 23, mouseX, mouseY)) session.archiveEntryId = entries.get(index).id(); + int visible = Math.max(1, (h - 34) / 62); + int start = Math.max(0, Math.min(session.archiveScroll, Math.max(0, entries.size() - visible))); + int index = start + ((int) mouseY - (archiveY + 16)) / 62; + int rowIndex = index - start; + if (rowIndex >= 0 && rowIndex < visible && index < entries.size() && inside(archiveX, archiveY + 16 + rowIndex * 62, w - 16, 56, mouseX, mouseY)) session.archiveEntryId = entries.get(index).id(); } return true; } @@ -576,7 +829,9 @@ public final class ComputerScreen extends Screen { for (String file : files) { String[] fields = file.split("\\t", 3); if (fields.length < 3 || !isDirectChild(fields[1], session.fileExplorerDirectory)) continue; - if (row++ != ((int) mouseY - (y + 62)) / 14) continue; + int rowTop = y + 34 + row++ * 14; + if (!inside(x, rowTop - 2, w, 14, mouseX, mouseY)) continue; + session.fileExplorerSelected = fields[1]; if (fields[0].equals("DIRECTORY")) session.fileExplorerDirectory = fields[1]; else if (fields[0].equals("TEXT")) { ComputerNetworking.openFile(position, fields[1]); @@ -624,10 +879,18 @@ public final class ComputerScreen extends Screen { setupAntmail(); return true; } - } else if (mouseY < y + 72) { - if (mouseX < x + 70) { session.antmailMode = "inbox"; session.antmailSent = false; session.antmailMessageIndex = -1; } - else if (mouseX < x + 125) { session.antmailMode = "sent"; session.antmailSent = true; session.antmailMessageIndex = -1; } - else { session.antmailMode = "compose"; session.antmailFocused = true; session.antmailField = 1; } + } else if (inside(x + 8, y + 54, 54, 27, mouseX, mouseY)) { + session.antmailMode = "inbox"; + session.antmailSent = false; + session.antmailMessageIndex = -1; + } else if (inside(x + 62, y + 54, 58, 27, mouseX, mouseY)) { + session.antmailMode = "sent"; + session.antmailSent = true; + session.antmailMessageIndex = -1; + } else if (inside(x + 122, y + 54, 92, 27, mouseX, mouseY)) { + session.antmailMode = "compose"; + session.antmailFocused = true; + session.antmailField = 1; } else if (session.antmailMode.equals("inbox") || session.antmailMode.equals("sent")) { int index = ((int) mouseY - (y + 108)) / 14; List messages = mailboxMessages(result, session.antmailSent); @@ -636,10 +899,22 @@ public final class ComputerScreen extends Screen { session.antmailMode = "message"; AntmailNetworking.markRead(position, messages.get(index).id()); } + } else if (session.antmailMode.equals("message")) { + if (mouseY < y + 72) { + session.antmailMode = session.antmailSent ? "sent" : "inbox"; + session.antmailMessageIndex = -1; + } } else if (session.antmailMode.equals("compose")) { if (mouseY >= y + 160 && mouseY < y + 184) { session.antmailAttachText = mouseX < x + 112; session.antmailAttachPaint = mouseX >= x + 112; + if (session.antmailAttachText && session.fileExplorerSelected.endsWith(".txt")) { + session.textPath = session.fileExplorerSelected; + ComputerNetworking.openFile(position, session.fileExplorerSelected); + } + if (session.antmailAttachPaint && session.fileExplorerSelected.endsWith(".antpaint")) { + ComputerNetworking.openFile(position, session.fileExplorerSelected); + } return true; } if (mouseY < y + 94) { @@ -663,19 +938,18 @@ public final class ComputerScreen extends Screen { return true; } } - if (window.type.equals("SETTINGS") && inside(x + 8, y + 70, 205, 25, mouseX, mouseY)) { - if (session.wallpapers.size() > 1) { - int current = session.wallpapers.indexOf(session.wallpaperId); - String next = session.wallpapers.get((current + 1 + session.wallpapers.size()) % session.wallpapers.size()); - session.wallpaperId = next; - ComputerNetworking.selectWallpaper(position, ResourceLocation.parse(next)); - } - return true; - } if (window.type.equals("SETTINGS") && inside(x + 8, y + 155, 205, 25, mouseX, mouseY)) { ejectSelected(); return true; } + if (window.type.equals("SETTINGS") && inside(x, y + h - 30, 90, 25, mouseX, mouseY)) { + ComputerNetworking.logout(position); + loggedIn = false; + session.authenticated = false; + session.windows.clear(); + activeWindow = null; + return true; + } if (inside(x, y + 20, w, h - 20, mouseX, mouseY)) return true; if (inside(x, y, w, 20, mouseX, mouseY)) { if (inside(x + w - 18, y, 18, 20, mouseX, mouseY)) session.windows.remove(i); @@ -741,6 +1015,15 @@ public final class ComputerScreen extends Screen { return true; } + @Override + public boolean mouseScrolled(double mouseX, double mouseY, double scrollX, double scrollY) { + if (loggedIn && activeWindow != null && activeWindow.type.equals("ARCHIVE") && session.archiveEntryId == null) { + session.archiveScroll = Math.max(0, session.archiveScroll - (int) Math.signum(scrollY)); + return true; + } + return super.mouseScrolled(mouseX, mouseY, scrollX, scrollY); + } + @Override public boolean charTyped(char codePoint, int modifiers) { if (!loggedIn && codePoint >= 32) { @@ -750,6 +1033,14 @@ public final class ComputerScreen extends Screen { password += Character.toUpperCase(codePoint); } } + if (loggedIn && activeWindow != null && activeWindow.type.equals("FILES") && session.fileExplorerRenaming && codePoint >= 32 && codePoint != '/' && codePoint != '\\') { + if (session.fileExplorerRename.length() < 48) session.fileExplorerRename += codePoint; + return true; + } + if (loggedIn && activeWindow != null && activeWindow.type.equals("TEXT") && session.textRenaming && codePoint >= 32 && codePoint != '/' && codePoint != '\\') { + if (session.textRename.length() < 48) session.textRename += codePoint; + return true; + } if (loggedIn && activeWindow != null) { if (activeWindow.type.equals("TEXT") && session.textFocused && codePoint >= 32) { session.textContent += codePoint; session.textDirty = true; return true; } if (activeWindow.type.equals("TERMINAL") && session.terminalFocused && codePoint >= 32) { session.terminalInput += codePoint; return true; } @@ -780,6 +1071,29 @@ public final class ComputerScreen extends Screen { if (keyCode == GLFW.GLFW_KEY_ESCAPE) { activeWindow.gameOpen = false; return true; } if (antFarm.keyPressed(keyCode)) return true; } + if (activeWindow != null && activeWindow.type.equals("FILES")) { + if (session.fileExplorerRenaming) { + if (keyCode == GLFW.GLFW_KEY_BACKSPACE) { + if (!session.fileExplorerRename.isEmpty()) session.fileExplorerRename = session.fileExplorerRename.substring(0, session.fileExplorerRename.length() - 1); + return true; + } + if (keyCode == GLFW.GLFW_KEY_ESCAPE) { + session.fileExplorerRenaming = false; + session.fileExplorerRename = ""; + return true; + } + if (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_KP_ENTER) { + renameSelectedFile(); + return true; + } + } else if (keyCode == GLFW.GLFW_KEY_F2) { + beginRenameSelectedFile(); + return true; + } else if (keyCode == GLFW.GLFW_KEY_DELETE) { + deleteSelectedFile(); + return true; + } + } if (activeWindow != null && activeWindow.type.equals("TERMINAL") && session.terminalFocused) { if (keyCode == GLFW.GLFW_KEY_BACKSPACE) { if (!session.terminalInput.isEmpty()) session.terminalInput = session.terminalInput.substring(0, session.terminalInput.length() - 1); return true; } if (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_KP_ENTER) { executeTerminal(); return true; } @@ -807,6 +1121,29 @@ public final class ComputerScreen extends Screen { if (keyCode == GLFW.GLFW_KEY_S && hasControl(modifiers)) { saveText(); return true; } if (keyCode == GLFW.GLFW_KEY_ESCAPE) { session.textFocused = false; return true; } } + if (activeWindow != null && activeWindow.type.equals("TEXT")) { + if (keyCode == GLFW.GLFW_KEY_F2 && !session.textPath.isBlank()) { + int slash = session.textPath.lastIndexOf('/'); + session.textRename = session.textPath.substring(slash + 1); + session.textRenaming = true; + return true; + } + if (session.textRenaming) { + if (keyCode == GLFW.GLFW_KEY_BACKSPACE) { + if (!session.textRename.isEmpty()) session.textRename = session.textRename.substring(0, session.textRename.length() - 1); + return true; + } + if (keyCode == GLFW.GLFW_KEY_ESCAPE) { + session.textRenaming = false; + session.textRename = ""; + return true; + } + if (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_KP_ENTER) { + renameTextFile(); + return true; + } + } + } if (activeWindow != null && activeWindow.type.equals("ARCHIVE") && session.archiveEntryId != null && keyCode == GLFW.GLFW_KEY_ESCAPE) { session.archiveEntryId = null; return true; @@ -848,25 +1185,7 @@ public final class ComputerScreen extends Screen { String input = session.terminalInput.trim(); if (input.isEmpty()) return; session.terminalOutput.add(session.terminalDirectory + "> " + input); - TerminalCommandParser.ParseResult parsed = TerminalCommandParser.parse(input); - if (!parsed.isSuccess()) session.terminalOutput.add(parsed.error()); - else { - String command = parsed.command().name(); - List args = parsed.command().arguments(); - if (command.equals("pwd")) session.terminalOutput.add(session.terminalDirectory); - else if (command.equals("clear")) session.terminalOutput.clear(); - else if (command.equals("ls")) ComputerNetworking.listFiles(position); - else if (command.equals("open") && args.size() == 1) ComputerNetworking.openFile(position, args.get(0)); - else if (command.equals("touch") && args.size() == 1) ComputerNetworking.createFile(position, args.get(0), ""); - else if (command.equals("write") && args.size() >= 1) ComputerNetworking.saveFile(position, args.get(0), args.size() > 1 ? args.get(1) : ""); - else if (command.equals("cd") && args.size() == 1) { - String path = args.get(0).startsWith("/") ? args.get(0) : session.terminalDirectory + "/" + args.get(0); - session.terminalDirectory = ComputerFileSystem.normalize(path); - } - else if (command.equals("help")) session.terminalOutput.add("HELP CLEAR PWD LS CD MKDIR TOUCH CAT WRITE RM RMDIR MV OPEN"); - else session.terminalOutput.add("COMMAND NOT RECOGNIZED"); - } - while (session.terminalOutput.size() > 40) session.terminalOutput.remove(0); + ComputerNetworking.terminalCommand(position, session.terminalDirectory, input); session.terminalInput = ""; } @@ -877,7 +1196,8 @@ public final class ComputerScreen extends Screen { return; } AntmailNetworking.setup(position, session.antmailUsername); - session.antmailStatus = "REGISTERING ADDRESS // WAITING"; + session.antmailStatus = "REGISTERING ADDRESS"; + session.antmailRegistrationPending = true; session.antmailFocused = false; } @@ -890,6 +1210,14 @@ public final class ComputerScreen extends Screen { session.antmailStatus = "SEND ERROR // SUBJECT AND BODY REQUIRED"; return; } + if (session.antmailAttachText && (session.textDirty || session.textPath.isBlank() || session.textPath.contains("untitled"))) { + session.antmailStatus = "SEND ERROR // SAVE TEXT FILE FIRST"; + return; + } + if (session.antmailAttachPaint && (session.paintDirty || session.paintName.isBlank() || session.paintName.startsWith("UNTITLED"))) { + session.antmailStatus = "SEND ERROR // SAVE PAINTING FIRST"; + return; + } List attachments = new ArrayList<>(); if (session.antmailAttachText && !session.textContent.isEmpty()) attachments.add(new AntmailAttachment.TextFile(session.textPath.substring(session.textPath.lastIndexOf('/') + 1), session.textContent)); if (session.antmailAttachPaint) { @@ -898,7 +1226,7 @@ public final class ComputerScreen extends Screen { attachments.add(new AntmailAttachment.PaintImage(session.paintName, AntPaintCanvas.WIDTH, AntPaintCanvas.HEIGHT, pixels)); } AntmailNetworking.send(position, session.antmailRecipient, session.antmailSubject, session.antmailBody, attachments); - session.antmailStatus = "TRANSMITTING MESSAGE // WAITING"; + session.antmailStatus = "MESSAGE SENT"; session.antmailMode = "inbox"; session.antmailFocused = false; session.antmailRecipient = ""; @@ -917,13 +1245,31 @@ public final class ComputerScreen extends Screen { } private void writeText() { - ComputerNetworking.createFile(position, session.textPath, session.textContent); + boolean exists = ComputerFileSystemClientState.get(position).files().stream().anyMatch(entry -> entry.contains("\t" + session.textPath + "\t")); + if (exists) ComputerNetworking.saveFile(position, session.textPath, session.textContent); + else ComputerNetworking.createFile(position, session.textPath, session.textContent); session.textDirty = false; + ComputerNetworking.listFiles(position); } private void saveText() { - ComputerNetworking.saveFile(position, session.textPath, session.textContent); + boolean exists = ComputerFileSystemClientState.get(position).files().stream().anyMatch(entry -> entry.contains("\t" + session.textPath + "\t")); + if (exists) ComputerNetworking.saveFile(position, session.textPath, session.textContent); + else ComputerNetworking.createFile(position, session.textPath, session.textContent); session.textDirty = false; + ComputerNetworking.listFiles(position); + } + + private void renameTextFile() { + if (!session.textRename.isBlank() && !session.textRename.contains("/") && !ComputerFileSystem.isProtected(session.textPath)) { + String parent = parentDirectory(session.textPath); + String destination = parent.equals("/") ? "/" + session.textRename : parent + "/" + session.textRename; + ComputerNetworking.moveFile(position, session.textPath, destination); + session.textPath = destination; + ComputerNetworking.listFiles(position); + } + session.textRenaming = false; + session.textRename = ""; } private void savePaint() { @@ -936,6 +1282,7 @@ public final class ComputerScreen extends Screen { boolean exists = ComputerFileSystemClientState.get(position).files().stream().anyMatch(entry -> entry.contains("\t" + savedPath + "\t")); if (exists) ComputerNetworking.saveFile(position, savedPath, encoded); else ComputerNetworking.createFile(position, savedPath, encoded); session.paintDirty = false; + ComputerNetworking.listFiles(position); } private void paintTool(int offset) { @@ -981,10 +1328,43 @@ public final class ComputerScreen extends Screen { selectedDisk = null; } + private void beginRenameSelectedFile() { + if (session.fileExplorerSelected.isEmpty() || session.fileExplorerSelected.equals("/") || ComputerFileSystem.isProtected(session.fileExplorerSelected)) return; + int slash = session.fileExplorerSelected.lastIndexOf('/'); + session.fileExplorerRename = session.fileExplorerSelected.substring(slash + 1); + session.fileExplorerRenaming = true; + } + + private void renameSelectedFile() { + if (!session.fileExplorerRename.isBlank() && !session.fileExplorerRename.contains("/")) { + String parent = parentDirectory(session.fileExplorerSelected); + String destination = parent.equals("/") ? "/" + session.fileExplorerRename : parent + "/" + session.fileExplorerRename; + ComputerNetworking.moveFile(position, session.fileExplorerSelected, destination); + session.fileExplorerSelected = destination; + ComputerNetworking.listFiles(position); + } + session.fileExplorerRenaming = false; + session.fileExplorerRename = ""; + } + + private void deleteSelectedFile() { + if (session.fileExplorerSelected.isEmpty() || session.fileExplorerSelected.equals("/") || ComputerFileSystem.isProtected(session.fileExplorerSelected)) return; + ComputerNetworking.deleteFile(position, session.fileExplorerSelected); + session.fileExplorerSelected = ""; + ComputerNetworking.listFiles(position); + } + private void open(String type) { - for (Window window : session.windows) if (window.type.equals(type)) { window.minimized = false; session.windows.remove(window); session.windows.add(window); return; } + for (Window window : session.windows) if (window.type.equals(type)) { + window.minimized = false; + session.windows.remove(window); + session.windows.add(window); + if (type.equals("FILES")) ComputerNetworking.listFiles(position); + return; + } session.windows.add(new Window(type, TITLES.get(type), 112 + session.windows.size() * 12, 52 + session.windows.size() * 10)); if (type.equals("ANTMAIL")) AntmailNetworking.requestState(position); + else if (type.equals("FILES")) ComputerNetworking.listFiles(position); } private static boolean inside(int x, int y, int w, int h, double mx, double my) { return mx >= x && mx < x + w && my >= y && my < y + h; } @@ -1005,13 +1385,21 @@ public final class ComputerScreen extends Screen { private final List terminalOutput = new ArrayList<>(List.of("ANTINTOSH TERMINAL [READY]", "TYPE HELP FOR COMMANDS")); private String terminalInput = ""; private String terminalDirectory = "/"; + private String terminalResponse = ""; private boolean terminalFocused; private String textPath = "/documents/untitled.txt"; private String textContent = ""; private boolean textDirty; private boolean textFocused; private boolean textListing; + private boolean textRenaming; + private String textRename = ""; private ResourceLocation archiveEntryId; + private int archiveScroll; + private int archiveMouseX; + private int archiveMouseY; + private int mouseX; + private int mouseY; private AntPaintCanvas paintCanvas = new AntPaintCanvas(); private final AntPaintHistory paintHistory = new AntPaintHistory(); private AntPaintTool paintTool = AntPaintTool.PENCIL; @@ -1019,6 +1407,7 @@ public final class ComputerScreen extends Screen { private boolean paintDirty; private String antmailUsername = ""; private String antmailStatus = ""; + private boolean antmailRegistrationPending; private boolean antmailFocused; private String antmailMode = "inbox"; private String antmailRecipient = ""; @@ -1030,6 +1419,9 @@ public final class ComputerScreen extends Screen { private boolean antmailAttachText; private boolean antmailAttachPaint; private String fileExplorerDirectory = "/"; + private String fileExplorerSelected = ""; + private String fileExplorerRename = ""; + private boolean fileExplorerRenaming; private String wallpaperId = ComputerDesktopState.DEFAULT_WALLPAPER.toString(); private List wallpapers = List.of(ComputerDesktopState.DEFAULT_WALLPAPER.toString()); private boolean desktopRequested; diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/ComputerDesktopState.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/ComputerDesktopState.java index 4356263a..87a45062 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/ComputerDesktopState.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/ComputerDesktopState.java @@ -144,8 +144,9 @@ public final class ComputerDesktopState { public void load(CompoundTag tag) { clear(); - selectedWallpaper = parseId(tag.getString(SELECTED_WALLPAPER_TAG)); loadIds(tag.getList(UNLOCKED_WALLPAPERS_TAG, 8), unlockedWallpaperIds, MAX_UNLOCKED_WALLPAPERS); + ResourceLocation storedSelection = parseId(tag.getString(SELECTED_WALLPAPER_TAG)); + selectedWallpaper = storedSelection != null && unlockedWallpaperIds.contains(storedSelection) ? storedSelection : DEFAULT_WALLPAPER; loadIds(tag.getList(INSTALLED_PROGRAMS_TAG, 8), installedProgramIds, MAX_INSTALLED_PROGRAMS); ListTag positions = tag.getList(ICON_POSITIONS_TAG, 10); for (int index = 0; index < positions.size() && iconPositions.size() < MAX_ICON_POSITIONS; index++) { diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/ComputerFileSystem.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/ComputerFileSystem.java index 28683555..4a5a0b04 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/ComputerFileSystem.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/computer/ComputerFileSystem.java @@ -52,6 +52,15 @@ public final class ComputerFileSystem { return nodes.values().stream().filter(file -> file.type() == ComputerFile.Type.TEXT).toList(); } + public Result createDirectory(String path) { + String normalized = normalize(path); + if (normalized.isEmpty() || ROOT.equals(normalized) || isProtected(normalized) || nodes.containsKey(normalized) || nodes.size() >= MAX_NODES) return Result.failure("invalid_path"); + int slash = normalized.lastIndexOf('/'); + if (slash > 0 && !nodesDirectory(normalized.substring(0, slash))) return Result.failure("missing_directory"); + nodes.put(normalized, ComputerFile.directory(normalized)); + return Result.ok(); + } + public Result createTextFile(String path, String contents) { String normalized = normalize(path); Result validation = validateTextPath(normalized, contents); @@ -83,6 +92,21 @@ public final class ComputerFileSystem { return nodes.containsKey(normalize(path)) ? writeTextFile(path, contents) : createTextFile(path, contents); } + public Result move(String source, String destination) { + String from = normalize(source); + String to = normalize(destination); + if (from.isEmpty() || to.isEmpty() || ROOT.equals(from) || ROOT.equals(to) || isProtected(from) || isProtected(to)) return Result.failure("invalid_path"); + ComputerFile file = nodes.get(from); + if (file == null) return Result.failure("not_found"); + if (nodes.containsKey(to)) return Result.failure("already_exists"); + int slash = to.lastIndexOf('/'); + if (slash > 0 && !nodesDirectory(to.substring(0, slash))) return Result.failure("missing_directory"); + if (file.type() == ComputerFile.Type.DIRECTORY && to.startsWith(from + "/")) return Result.failure("invalid_path"); + nodes.remove(from); + nodes.put(to, file.type() == ComputerFile.Type.DIRECTORY ? ComputerFile.directory(to) : ComputerFile.text(to, file.contents())); + return Result.ok(); + } + public Result delete(String path) { String normalized = normalize(path); if (isProtected(normalized)) return Result.failure("protected"); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/creative/CreativeTabContents.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/creative/CreativeTabContents.java index 7042a97a..6deea10d 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/creative/CreativeTabContents.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/creative/CreativeTabContents.java @@ -639,7 +639,8 @@ public final class CreativeTabContents { for (String potionPath : ANTARCHY_POTION_FAMILIES) { acceptPotionFamily(output, potionPath); } - for (String diskId : List.of("antarchy:introduction", "antarchy:chronosphere", "antarchy:toreterror_arena", "antarchy:high_fructose_corn_syrup")) { + for (String diskId : List.of("antarchy:introduction", "antarchy:chronosphere", "antarchy:toreterror_arena", "antarchy:high_fructose_corn_syrup", + "antarchy:elythia", "antarchy:cavaryn", "antarchy:thoraxis", "antarchy:temporal_fields", "antarchy:bloodglass", "antarchy:vortex")) { ItemStack disk = new ItemStack(AntarchyObjects.FLOPPY_DISK.get()); disk.set(AntarchyObjects.FLOPPY_DISK_COMPONENT.get(), ResourceLocation.parse(diskId)); output.accept(disk); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/guide/ComputerGuideData.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/guide/ComputerGuideData.java index 666fd006..0af87166 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/guide/ComputerGuideData.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/guide/ComputerGuideData.java @@ -1,5 +1,6 @@ package com.craisinlord.antarchy.content.guide; +import com.craisinlord.antarchy.config.AntarchySettings; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -38,10 +39,16 @@ public final class ComputerGuideData extends SimplePreparableReloadListener entriesFor(List diskIds) { + if (AntarchySettings.unlockAllArchives()) { + Map result = new LinkedHashMap<>(); + result.put(INTRODUCTION, new Entry(INTRODUCTION, "article", "general", "guide.antarchy.entry.introduction.title", "guide.antarchy.entry.introduction.subtitle", List.of("guide.antarchy.entry.introduction.description"), "", "", "", "")); + result.putAll(entries); + return result.values().stream().sorted(Comparator.comparing(entry -> entry.titleKey().toString())).toList(); + } Map result = new LinkedHashMap<>(); for (ResourceLocation diskId : diskIds) { if (INTRODUCTION.equals(diskId)) { - result.put(INTRODUCTION, new Entry(INTRODUCTION, "article", "general", "guide.antarchy.entry.introduction.title", "guide.antarchy.entry.introduction.subtitle", List.of("guide.antarchy.entry.introduction.description"), "", "", "")); + result.put(INTRODUCTION, new Entry(INTRODUCTION, "article", "general", "guide.antarchy.entry.introduction.title", "guide.antarchy.entry.introduction.subtitle", List.of("guide.antarchy.entry.introduction.description"), "", "", "", "")); continue; } Disk disk = disks.get(diskId); @@ -107,9 +114,10 @@ public final class ComputerGuideData extends SimplePreparableReloadListener description = strings(object.get("description")); String item = string(object, "item", ""); + String entity = string(object, "entity", ""); String structure = string(object, "structure", ""); String dimension = string(object, "dimension", ""); - target.put(id, new Entry(id, type, category, title, subtitle, description, item, structure, dimension)); + target.put(id, new Entry(id, type, category, title, subtitle, description, item, entity, structure, dimension)); } private static void parseDisk(ResourceLocation id, JsonElement element, Map target) { @@ -131,7 +139,8 @@ public final class ComputerGuideData extends SimplePreparableReloadListener descriptionKeys, String itemId, String structureId, String dimensionId) { + List descriptionKeys, String itemId, String entityId, String structureId, String dimensionId) { } - public record Disk(ResourceLocation id, String category, String titleKey, List entries) { + public record Disk(ResourceLocation id, String category, String titleKey, List entries, String wallpaper) { } record LoadedData(Map entries, Map disks) { diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/horde/CavarynHordeManager.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/horde/CavarynHordeManager.java index d8347559..7909b836 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/horde/CavarynHordeManager.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/horde/CavarynHordeManager.java @@ -69,6 +69,8 @@ public final class CavarynHordeManager { private static final int GROUP_RADIUS_BLOCKS = 128; private static final int ATTENTION_THRESHOLD = 100; + private static final int TIGERS_EYE_TRIGGER_LEVEL = 125; + private static final ResourceLocation TIGERS_EYE_BLOCK = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "tigers_eye_block"); private static final int HOSTILE_KILL_ATTENTION = 4; private static final int MOB_KILL_ATTENTION = 3; private static final int EGG_OR_NEST_BREAK_ATTENTION = 5; @@ -129,7 +131,11 @@ public final class CavarynHordeManager { } public static void recordBlockBreak(ServerPlayer player, BlockState brokenState, BlockPos pos) { - if (!(player.level() instanceof ServerLevel level) || !level.dimension().equals(CAVARYN) || !hordesEnabled(level)) { + if (!(player.level() instanceof ServerLevel level)) { + return; + } + triggerTigersEyeBlockBreak(player, level, brokenState); + if (!level.dimension().equals(CAVARYN) || !hordesEnabled(level)) { return; } if (!isValidHordeTarget(player) || !isHordeBiome(level, pos) || isPlayerInActiveEncounter(level, player)) { @@ -138,6 +144,24 @@ public final class CavarynHordeManager { addAttention(level, player, scaleActionAttention(level, isEggOrNest(brokenState) ? EGG_OR_NEST_BREAK_ATTENTION : BLOCK_BREAK_ATTENTION)); } + private static void triggerTigersEyeBlockBreak(ServerPlayer player, ServerLevel level, BlockState brokenState) { + if (!level.dimension().equals(CAVARYN) + || !hordesEnabled(level) + || !isValidHordeTarget(player) + || !BuiltInRegistries.BLOCK.getKey(brokenState.getBlock()).equals(TIGERS_EYE_BLOCK)) { + return; + } + + HordeData data = get(level); + PlayerAttention attention = data.players.computeIfAbsent(player.getUUID(), ignored -> new PlayerAttention()); + attention.attention = TIGERS_EYE_TRIGGER_LEVEL; + attention.areaKey = areaKey(player.blockPosition()); + attention.areaTicks = 0; + attention.lastSeenGameTime = level.getGameTime(); + data.setDirty(); + HordeIntensitySync.send(player, attention.attention / (float) ATTENTION_THRESHOLD); + } + public static boolean isHordeBiome(ServerLevelAccessor level, BlockPos pos) { return level.getBiome(pos).is(AntarchyTags.Biomes.USES_HORDE_SYSTEM); } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailServerHandler.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailServerHandler.java index 4ce459de..c2e78a36 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailServerHandler.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/AntmailServerHandler.java @@ -30,12 +30,14 @@ public final class AntmailServerHandler { public static void handle(ServerPlayer player, AntmailSetupPayload payload) { ComputerBlockEntity computer = computer(player, payload.pos()); if (computer == null || !computer.canUseFileSystem(player)) { - result(player, payload.pos(), AntmailDeliveryResult.Status.FAILED.ordinal(), "", "", "unauthorized", ""); + result(player, payload.pos(), AntmailDeliveryResult.Status.FAILED.ordinal(), "", "", "registration_failed:unauthorized", ""); return; } AntmailServerData data = AntmailServerData.access(player.server); AntmailServerData.Registration registration = data.register(player.server, player.serverLevel(), payload.pos(), payload.username()); - result(player, payload.pos(), registration.valid() ? AntmailDeliveryResult.Status.DELIVERED.ordinal() : AntmailDeliveryResult.Status.FAILED.ordinal(), registration.valid() ? registration.address().fullAddress() : "", "", registration.reason(), ""); + String mailbox = registration.valid() ? AntmailWire.encodeTag(data.mailboxOrCreate(registration.address()).toTag()) : ""; + String detail = registration.valid() ? "registration_success" : "registration_failed:" + registration.reason(); + result(player, payload.pos(), registration.valid() ? AntmailDeliveryResult.Status.DELIVERED.ordinal() : AntmailDeliveryResult.Status.FAILED.ordinal(), registration.valid() ? registration.address().fullAddress() : "", "", detail, mailbox); } public static void handle(ServerPlayer player, AntmailStateRequestPayload payload) { @@ -71,7 +73,7 @@ public final class AntmailServerHandler { List attachments = AntmailWire.decodeAttachments(payload.attachments()); AntmailMessage message = AntmailMessage.create(sender, recipient, payload.subject(), payload.body(), player.serverLevel().getGameTime(), attachments); AntmailDeliveryResult delivery = data.deliver(player.server, message); - result(player, payload.pos(), delivery.status().ordinal(), recipient.fullAddress(), message.id().toString(), delivery.detail(), ""); + result(player, payload.pos(), delivery.status().ordinal(), sender.fullAddress(), message.id().toString(), delivery.detail(), AntmailWire.encodeTag(data.mailboxOrCreate(sender).toTag())); } catch (RuntimeException exception) { result(player, payload.pos(), AntmailDeliveryResult.Status.MESSAGE_INVALID.ordinal(), "", "", "invalid_message", ""); } @@ -94,7 +96,7 @@ public final class AntmailServerHandler { AntmailMailbox mailbox = data.mailboxOrCreate(address); boolean changed = payload.read() ? mailbox.markRead(id) : markUnread(mailbox, id); if (changed) data.setDirty(); - result(player, payload.pos(), changed ? AntmailDeliveryResult.Status.DELIVERED.ordinal() : AntmailDeliveryResult.Status.FAILED.ordinal(), address.fullAddress(), payload.messageId(), changed ? "" : "not_found", ""); + result(player, payload.pos(), changed ? AntmailDeliveryResult.Status.DELIVERED.ordinal() : AntmailDeliveryResult.Status.FAILED.ordinal(), address.fullAddress(), payload.messageId(), changed ? "" : "not_found", AntmailWire.encodeTag(mailbox.toTag())); } catch (IllegalArgumentException exception) { result(player, payload.pos(), AntmailDeliveryResult.Status.FAILED.ordinal(), address.fullAddress(), payload.messageId(), "invalid_id", ""); } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/ComputerAccessHandler.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/ComputerAccessHandler.java index 1a2c6b9b..5c1bca8d 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/ComputerAccessHandler.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/ComputerAccessHandler.java @@ -2,6 +2,9 @@ package com.craisinlord.antarchy.content.network; import com.craisinlord.antarchy.content.block.entity.ComputerBlockEntity; import com.craisinlord.antarchy.content.computer.ComputerFileSystem; +import com.craisinlord.antarchy.content.computer.terminal.TerminalCommandService; +import com.craisinlord.antarchy.content.computer.terminal.TerminalFileSystem; +import com.craisinlord.antarchy.content.computer.terminal.TerminalResult; import com.craisinlord.antarchy.content.antmail.AntmailWire; import net.minecraft.server.level.ServerPlayer; import net.minecraft.resources.ResourceLocation; @@ -51,6 +54,9 @@ public final class ComputerAccessHandler { case ComputerAccessPayload.FILE_OPEN -> fileOpen(player, computer, payload); case ComputerAccessPayload.FILE_CREATE -> fileCreate(player, computer, payload); case ComputerAccessPayload.FILE_SAVE -> fileSave(player, computer, payload); + case ComputerAccessPayload.FILE_DELETE -> fileDelete(player, computer, payload); + case ComputerAccessPayload.FILE_MOVE -> fileMove(player, computer, payload); + case ComputerAccessPayload.TERMINAL_COMMAND -> terminalCommand(player, computer, payload); case ComputerAccessPayload.DESKTOP_STATE -> desktopState(player, computer, payload); case ComputerAccessPayload.DESKTOP_WALLPAPER -> desktopWallpaper(player, computer, payload); default -> send(player, payload, ComputerAccessResultPayload.INVALID); @@ -159,6 +165,89 @@ public final class ComputerAccessHandler { sendFile(player, payload, result.successful(), request[0], result.error()); } + private static void fileDelete(ServerPlayer player, ComputerBlockEntity computer, ComputerAccessPayload payload) { + if (!computer.canUseFileSystem(player)) { + sendFile(player, payload, false, "", "unauthorized"); + return; + } + ComputerFileSystem.Result result = computer.fileSystem().delete(payload.value()); + if (result.successful()) computer.setChanged(); + sendFile(player, payload, result.successful(), payload.value(), result.error()); + } + + private static void fileMove(ServerPlayer player, ComputerBlockEntity computer, ComputerAccessPayload payload) { + if (!computer.canUseFileSystem(player)) { + sendFile(player, payload, false, "", "unauthorized"); + return; + } + String[] request = splitFileRequest(payload.value()); + if (request == null) { + sendFile(player, payload, false, "", "invalid_request"); + return; + } + ComputerFileSystem.Result result = computer.fileSystem().move(request[0], request[1]); + if (result.successful()) computer.setChanged(); + sendFile(player, payload, result.successful(), request[1], result.error()); + } + + private static void terminalCommand(ServerPlayer player, ComputerBlockEntity computer, ComputerAccessPayload payload) { + if (!computer.canUseFileSystem(player)) { + send(player, payload, ComputerAccessResultPayload.INVALID); + return; + } + String[] request = splitFileRequest(payload.value()); + if (request == null) { + sendTerminal(player, payload, TerminalResult.error("/", "ERROR: INVALID REQUEST")); + return; + } + TerminalResult result = new TerminalCommandService().execute(new ComputerTerminalFileSystem(computer.fileSystem()), request[0], request[1]); + if (result.status() != TerminalResult.Status.SUCCESS || !result.lines().isEmpty() || result.clearOutput() || result.openPath() != null) computer.setChanged(); + sendTerminal(player, payload, result); + } + + private static void sendTerminal(ServerPlayer player, ComputerAccessPayload payload, TerminalResult result) { + net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag(); + tag.putString("Directory", result.workingDirectory()); + tag.putBoolean("Clear", result.clearOutput()); + tag.putString("Open", result.openPath() == null ? "" : result.openPath()); + net.minecraft.nbt.ListTag lines = new net.minecraft.nbt.ListTag(); + for (String line : result.lines()) lines.add(net.minecraft.nbt.StringTag.valueOf(line)); + tag.put("Lines", lines); + resultSender.accept(player, new ComputerAccessResultPayload(payload.pos(), result.status() == TerminalResult.Status.ERROR ? ComputerAccessResultPayload.INVALID : ComputerAccessResultPayload.SUCCESS, + true, true, payload.action() + "\0" + AntmailWire.encodeTag(tag))); + } + + private static final class ComputerTerminalFileSystem implements TerminalFileSystem { + private final ComputerFileSystem fileSystem; + + private ComputerTerminalFileSystem(ComputerFileSystem fileSystem) { this.fileSystem = fileSystem; } + + @Override public java.util.Optional find(String path, String workingDirectory) { + ComputerFileSystem.ComputerFile file = fileSystem.get(resolve(path, workingDirectory)); + return file == null ? java.util.Optional.empty() : java.util.Optional.of(new Entry(file.path(), file.path(), file.type() == ComputerFileSystem.ComputerFile.Type.DIRECTORY ? EntryType.DIRECTORY : EntryType.FILE)); + } + @Override public java.util.List list(String path, String workingDirectory) { + String directory = resolve(path, workingDirectory); + return fileSystem.list().stream().filter(file -> { + String parent = file.path().lastIndexOf('/') <= 0 ? "/" : file.path().substring(0, file.path().lastIndexOf('/')); + return parent.equals(directory) && !file.path().equals(directory); + }).map(file -> new Entry(file.path().substring(file.path().lastIndexOf('/') + 1), file.path(), file.type() == ComputerFileSystem.ComputerFile.Type.DIRECTORY ? EntryType.DIRECTORY : EntryType.FILE)).toList(); + } + @Override public boolean createDirectory(String path, String workingDirectory) { return fileSystem.createDirectory(resolve(path, workingDirectory)).successful(); } + @Override public boolean createFile(String path, String workingDirectory) { return fileSystem.createTextFile(resolve(path, workingDirectory), "").successful(); } + @Override public java.util.Optional readText(String path, String workingDirectory) { ComputerFileSystem.ComputerFile file = fileSystem.get(resolve(path, workingDirectory)); return file != null && file.type() == ComputerFileSystem.ComputerFile.Type.TEXT ? java.util.Optional.of(file.contents()) : java.util.Optional.empty(); } + @Override public boolean writeText(String path, String workingDirectory, String contents) { return fileSystem.createOrWriteTextFile(resolve(path, workingDirectory), contents).successful(); } + @Override public boolean deleteFile(String path, String workingDirectory) { return fileSystem.delete(resolve(path, workingDirectory)).successful(); } + @Override public boolean deleteEmptyDirectory(String path, String workingDirectory) { return fileSystem.delete(resolve(path, workingDirectory)).successful(); } + @Override public boolean move(String source, String destination, String workingDirectory) { return fileSystem.move(resolve(source, workingDirectory), resolve(destination, workingDirectory)).successful(); } + @Override public String normalize(String path, String workingDirectory) { return resolve(path, workingDirectory); } + + private String resolve(String path, String workingDirectory) { + if (path == null || path.isBlank()) return ComputerFileSystem.normalize(workingDirectory); + return ComputerFileSystem.normalize(path.startsWith("/") ? path : ComputerFileSystem.normalize(workingDirectory) + "/" + path); + } + } + private static void desktopState(ServerPlayer player, ComputerBlockEntity computer, ComputerAccessPayload payload) { if (!computer.canUseFileSystem(player)) { sendDesktop(player, payload, false, "unauthorized"); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/ComputerAccessPayload.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/ComputerAccessPayload.java index bcc96371..5cc418e7 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/ComputerAccessPayload.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/ComputerAccessPayload.java @@ -22,6 +22,9 @@ public record ComputerAccessPayload(BlockPos pos, int action, String value) impl public static final int FILE_SAVE = 10; public static final int DESKTOP_STATE = 11; public static final int DESKTOP_WALLPAPER = 12; + public static final int FILE_DELETE = 13; + public static final int FILE_MOVE = 14; + public static final int TERMINAL_COMMAND = 15; public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "computer_access")); public static final StreamCodec STREAM_CODEC = StreamCodec.composite( BlockPos.STREAM_CODEC, ComputerAccessPayload::pos, diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/ComputerNetworking.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/ComputerNetworking.java index 019e24db..e3bac6f6 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/ComputerNetworking.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/network/ComputerNetworking.java @@ -60,6 +60,18 @@ public final class ComputerNetworking { sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.FILE_SAVE, path + "\0" + contents)); } + public static void deleteFile(BlockPos pos, String path) { + sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.FILE_DELETE, path)); + } + + public static void moveFile(BlockPos pos, String source, String destination) { + sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.FILE_MOVE, source + "\0" + destination)); + } + + public static void terminalCommand(BlockPos pos, String directory, String command) { + sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.TERMINAL_COMMAND, directory + "\0" + command)); + } + public static void requestDesktopState(BlockPos pos) { sender.accept(new ComputerAccessPayload(pos, ComputerAccessPayload.DESKTOP_STATE)); } diff --git a/common-1.21.1/src/main/resources/assets/antarchy/lang/en_us.json b/common-1.21.1/src/main/resources/assets/antarchy/lang/en_us.json index 3e60ab18..74801f35 100644 --- a/common-1.21.1/src/main/resources/assets/antarchy/lang/en_us.json +++ b/common-1.21.1/src/main/resources/assets/antarchy/lang/en_us.json @@ -1456,6 +1456,12 @@ ,"guide.antarchy.disk.chronosphere.title": "CHRONOSPHERE DATA" ,"guide.antarchy.disk.toreterror_arena.title": "TORETERROR ARENA DATA" ,"guide.antarchy.disk.high_fructose_corn_syrup.title": "CORN SYRUP DATA" + ,"guide.antarchy.disk.elythia.title": "ELYTHIA ACCESS RECORD" + ,"guide.antarchy.disk.cavaryn.title": "CAVARYN ACCESS RECORD" + ,"guide.antarchy.disk.thoraxis.title": "THORAXIS ACCESS RECORD" + ,"guide.antarchy.disk.temporal_fields.title": "TEMPORAL FIELDS MANUAL" + ,"guide.antarchy.disk.bloodglass.title": "BLOODGLASS SURVIVAL NOTES" + ,"guide.antarchy.disk.vortex.title": "VORTEX CONTROL NOTES" ,"guide.antarchy.entry.introduction.title": "SYSTEM INITIALIZED" ,"guide.antarchy.entry.introduction.subtitle": "A machine for remembering what should have stayed forgotten." ,"guide.antarchy.entry.introduction.description": "Insert recovered floppy disks to expand this archive. The machine remembers. The machine does not forgive." @@ -1465,6 +1471,27 @@ ,"guide.antarchy.entry.toreterror_arena.title": "Toreterror Arena" ,"guide.antarchy.entry.toreterror_arena.subtitle": "Structure location fragment" ,"guide.antarchy.entry.toreterror_arena.description": "The nearest arena is rumored to lie beyond the current horizon. Directional analysis is pending in this prototype." + ,"guide.antarchy.entry.elythia.title": "Elythia" + ,"guide.antarchy.entry.elythia.subtitle": "Dimension and portal access record" + ,"guide.antarchy.entry.elythia.description": "Elythia is reached through an Elythia portal made with a valid frame. A sacrificed Brown Ant activates the portal. The dimension contains oceans, meadows, forests, coral spikes, and other surface biomes." + ,"guide.antarchy.entry.cavaryn.title": "Cavaryn" + ,"guide.antarchy.entry.cavaryn.subtitle": "Dimension and portal access record" + ,"guide.antarchy.entry.cavaryn.description": "Cavaryn is reached through a Cavaryn portal made with a valid frame. A sacrificed Termite activates the portal. The dimension is a deep network of caves, including Glowcap, Biowart, Moleworm, Wormy, and Brooding Tunnel regions." + ,"guide.antarchy.entry.thoraxis.title": "Thoraxis" + ,"guide.antarchy.entry.thoraxis.subtitle": "Dimension and portal access record" + ,"guide.antarchy.entry.thoraxis.description": "Thoraxis is reached through a Thoraxis portal made with a valid frame. A sacrificed Red Ant activates the portal. Its vertical progression includes Dream Dunes, Umbral Hills, Lucid Pools, and Nadir Forest." + ,"guide.antarchy.entry.temporal_fields.title": "Temporal Fields" + ,"guide.antarchy.entry.temporal_fields.subtitle": "Time dilation and Chronosphere reference" + ,"guide.antarchy.entry.temporal_fields.description": "Time Dilation and Time Contraction alter the rate of affected entities. Chronosphere armor creates a player-centered field at 35 percent rate; higher levels expand its radius." + ,"guide.antarchy.entry.bloodglass.title": "Bloodglass" + ,"guide.antarchy.entry.bloodglass.subtitle": "Reactive protection system" + ,"guide.antarchy.entry.bloodglass.description": "Blood Crystal armor grants Bloodglass Hearts that block an incoming hit, shatter, and recharge. Blood Crystal Apples grant temporary Bloodglass Ward, and the Katana provides launch, dash, and crimson-trail mobility." + ,"guide.antarchy.entry.vortex.title": "Vortex Systems" + ,"guide.antarchy.entry.vortex.subtitle": "Vortex Lens and Eye of the Storm reference" + ,"guide.antarchy.entry.vortex.description": "Redstone power pulls entities into a Vortex Lens beam while Bluestone power blasts them away. The Eye of the Storm launches an updraft and can hurl a homing tornado that carries foes." + ,"guide.antarchy.entry.nightmare.title": "Nightmare" + ,"guide.antarchy.entry.nightmare.subtitle": "Thoraxis hostile entity record" + ,"guide.antarchy.entry.nightmare.description": "A hostile entity recorded in the deepest Thoraxis regions. The recovered model is presented in monochrome archive mode." ,"guide.antarchy.entry.high_fructose_corn_syrup.title": "High Fructose Corn Syrup" ,"guide.antarchy.entry.high_fructose_corn_syrup.subtitle": "Nutritional and operational notes" ,"guide.antarchy.entry.high_fructose_corn_syrup.description": "A sweet substance of uncertain strategic value. Doctoral enthusiasm surrounding it is statistically suspicious." diff --git a/common-1.21.1/src/main/resources/data/antarchy/computer/disk/bloodglass.json b/common-1.21.1/src/main/resources/data/antarchy/computer/disk/bloodglass.json new file mode 100644 index 00000000..fcb45ca4 --- /dev/null +++ b/common-1.21.1/src/main/resources/data/antarchy/computer/disk/bloodglass.json @@ -0,0 +1,6 @@ +{ + "category": "thoraxis", + "title": "guide.antarchy.disk.bloodglass.title", + "entries": ["antarchy:bloodglass"], + "wallpaper": "antarchy:bloodglass" +} diff --git a/common-1.21.1/src/main/resources/data/antarchy/computer/disk/cavaryn.json b/common-1.21.1/src/main/resources/data/antarchy/computer/disk/cavaryn.json new file mode 100644 index 00000000..c825861b --- /dev/null +++ b/common-1.21.1/src/main/resources/data/antarchy/computer/disk/cavaryn.json @@ -0,0 +1,6 @@ +{ + "category": "cavaryn", + "title": "guide.antarchy.disk.cavaryn.title", + "entries": ["antarchy:cavaryn"], + "wallpaper": "antarchy:cavaryn" +} diff --git a/common-1.21.1/src/main/resources/data/antarchy/computer/disk/elythia.json b/common-1.21.1/src/main/resources/data/antarchy/computer/disk/elythia.json new file mode 100644 index 00000000..d0ad36f8 --- /dev/null +++ b/common-1.21.1/src/main/resources/data/antarchy/computer/disk/elythia.json @@ -0,0 +1,6 @@ +{ + "category": "elythia", + "title": "guide.antarchy.disk.elythia.title", + "entries": ["antarchy:elythia"], + "wallpaper": "antarchy:elythia" +} diff --git a/common-1.21.1/src/main/resources/data/antarchy/computer/disk/temporal_fields.json b/common-1.21.1/src/main/resources/data/antarchy/computer/disk/temporal_fields.json new file mode 100644 index 00000000..826b53af --- /dev/null +++ b/common-1.21.1/src/main/resources/data/antarchy/computer/disk/temporal_fields.json @@ -0,0 +1,6 @@ +{ + "category": "thoraxis", + "title": "guide.antarchy.disk.temporal_fields.title", + "entries": ["antarchy:temporal_fields"], + "wallpaper": "antarchy:temporal_fields" +} diff --git a/common-1.21.1/src/main/resources/data/antarchy/computer/disk/thoraxis.json b/common-1.21.1/src/main/resources/data/antarchy/computer/disk/thoraxis.json new file mode 100644 index 00000000..a7e052aa --- /dev/null +++ b/common-1.21.1/src/main/resources/data/antarchy/computer/disk/thoraxis.json @@ -0,0 +1,6 @@ +{ + "category": "thoraxis", + "title": "guide.antarchy.disk.thoraxis.title", + "entries": ["antarchy:thoraxis", "antarchy:nightmare"], + "wallpaper": "antarchy:thoraxis" +} diff --git a/common-1.21.1/src/main/resources/data/antarchy/computer/disk/vortex.json b/common-1.21.1/src/main/resources/data/antarchy/computer/disk/vortex.json new file mode 100644 index 00000000..4106610b --- /dev/null +++ b/common-1.21.1/src/main/resources/data/antarchy/computer/disk/vortex.json @@ -0,0 +1,6 @@ +{ + "category": "thoraxis", + "title": "guide.antarchy.disk.vortex.title", + "entries": ["antarchy:vortex"], + "wallpaper": "antarchy:vortex" +} diff --git a/common-1.21.1/src/main/resources/data/antarchy/computer/entry/bloodglass.json b/common-1.21.1/src/main/resources/data/antarchy/computer/entry/bloodglass.json new file mode 100644 index 00000000..bed99ccf --- /dev/null +++ b/common-1.21.1/src/main/resources/data/antarchy/computer/entry/bloodglass.json @@ -0,0 +1,7 @@ +{ + "type": "article", + "category": "thoraxis", + "title": "guide.antarchy.entry.bloodglass.title", + "subtitle": "guide.antarchy.entry.bloodglass.subtitle", + "description": ["guide.antarchy.entry.bloodglass.description"] +} diff --git a/common-1.21.1/src/main/resources/data/antarchy/computer/entry/cavaryn.json b/common-1.21.1/src/main/resources/data/antarchy/computer/entry/cavaryn.json new file mode 100644 index 00000000..7f03da60 --- /dev/null +++ b/common-1.21.1/src/main/resources/data/antarchy/computer/entry/cavaryn.json @@ -0,0 +1,8 @@ +{ + "type": "dimension", + "category": "cavaryn", + "title": "guide.antarchy.entry.cavaryn.title", + "subtitle": "guide.antarchy.entry.cavaryn.subtitle", + "description": ["guide.antarchy.entry.cavaryn.description"], + "dimension": "antarchy:cavaryn" +} diff --git a/common-1.21.1/src/main/resources/data/antarchy/computer/entry/elythia.json b/common-1.21.1/src/main/resources/data/antarchy/computer/entry/elythia.json new file mode 100644 index 00000000..47a52165 --- /dev/null +++ b/common-1.21.1/src/main/resources/data/antarchy/computer/entry/elythia.json @@ -0,0 +1,8 @@ +{ + "type": "dimension", + "category": "elythia", + "title": "guide.antarchy.entry.elythia.title", + "subtitle": "guide.antarchy.entry.elythia.subtitle", + "description": ["guide.antarchy.entry.elythia.description"], + "dimension": "antarchy:elythia" +} diff --git a/common-1.21.1/src/main/resources/data/antarchy/computer/entry/nightmare.json b/common-1.21.1/src/main/resources/data/antarchy/computer/entry/nightmare.json new file mode 100644 index 00000000..6fda6806 --- /dev/null +++ b/common-1.21.1/src/main/resources/data/antarchy/computer/entry/nightmare.json @@ -0,0 +1,8 @@ +{ + "type": "mob", + "category": "thoraxis", + "title": "guide.antarchy.entry.nightmare.title", + "subtitle": "guide.antarchy.entry.nightmare.subtitle", + "description": ["guide.antarchy.entry.nightmare.description"], + "entity": "antarchy:nightmare" +} diff --git a/common-1.21.1/src/main/resources/data/antarchy/computer/entry/temporal_fields.json b/common-1.21.1/src/main/resources/data/antarchy/computer/entry/temporal_fields.json new file mode 100644 index 00000000..5144dd2e --- /dev/null +++ b/common-1.21.1/src/main/resources/data/antarchy/computer/entry/temporal_fields.json @@ -0,0 +1,7 @@ +{ + "type": "article", + "category": "thoraxis", + "title": "guide.antarchy.entry.temporal_fields.title", + "subtitle": "guide.antarchy.entry.temporal_fields.subtitle", + "description": ["guide.antarchy.entry.temporal_fields.description"] +} diff --git a/common-1.21.1/src/main/resources/data/antarchy/computer/entry/thoraxis.json b/common-1.21.1/src/main/resources/data/antarchy/computer/entry/thoraxis.json new file mode 100644 index 00000000..8579f75c --- /dev/null +++ b/common-1.21.1/src/main/resources/data/antarchy/computer/entry/thoraxis.json @@ -0,0 +1,8 @@ +{ + "type": "dimension", + "category": "thoraxis", + "title": "guide.antarchy.entry.thoraxis.title", + "subtitle": "guide.antarchy.entry.thoraxis.subtitle", + "description": ["guide.antarchy.entry.thoraxis.description"], + "dimension": "antarchy:thoraxis" +} diff --git a/common-1.21.1/src/main/resources/data/antarchy/computer/entry/vortex.json b/common-1.21.1/src/main/resources/data/antarchy/computer/entry/vortex.json new file mode 100644 index 00000000..915f2f9c --- /dev/null +++ b/common-1.21.1/src/main/resources/data/antarchy/computer/entry/vortex.json @@ -0,0 +1,7 @@ +{ + "type": "article", + "category": "thoraxis", + "title": "guide.antarchy.entry.vortex.title", + "subtitle": "guide.antarchy.entry.vortex.subtitle", + "description": ["guide.antarchy.entry.vortex.description"] +} diff --git a/common-1.21.1/src/main/resources/data/antarchy/loot_table/chests/computer_prototype.json b/common-1.21.1/src/main/resources/data/antarchy/loot_table/chests/computer_prototype.json index 625a8f83..7e2bfce1 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/loot_table/chests/computer_prototype.json +++ b/common-1.21.1/src/main/resources/data/antarchy/loot_table/chests/computer_prototype.json @@ -16,6 +16,32 @@ } } ] + }, + { + "type": "minecraft:item", + "name": "antarchy:floppy_disk", + "weight": 4, + "functions": [ + { + "function": "minecraft:set_components", + "components": { + "antarchy:floppy_disk": "antarchy:elythia" + } + } + ] + }, + { + "type": "minecraft:item", + "name": "antarchy:floppy_disk", + "weight": 4, + "functions": [ + { + "function": "minecraft:set_components", + "components": { + "antarchy:floppy_disk": "antarchy:cavaryn" + } + } + ] } ] } diff --git a/common-1.21.1/src/main/resources/data/antarchy/loot_table/chests/tigers_eye_shrine/treasure.json b/common-1.21.1/src/main/resources/data/antarchy/loot_table/chests/tigers_eye_shrine/treasure.json new file mode 100644 index 00000000..d975eaf6 --- /dev/null +++ b/common-1.21.1/src/main/resources/data/antarchy/loot_table/chests/tigers_eye_shrine/treasure.json @@ -0,0 +1,162 @@ +{ + "type": "minecraft:chest", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "antarchy:uranium_ingot", + "functions": [ + { + "function": "minecraft:set_count", + "count": { + "type": "minecraft:uniform", + "min": 1, + "max": 2 + } + } + ] + } + ] + }, + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "antarchy:tigers_eye", + "functions": [ + { + "function": "minecraft:set_count", + "count": { + "type": "minecraft:uniform", + "min": 1, + "max": 3 + } + } + ] + } + ] + }, + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "antarchy:titanium_nugget", + "functions": [ + { + "function": "minecraft:set_count", + "count": { + "type": "minecraft:uniform", + "min": 2, + "max": 6 + } + } + ] + } + ] + }, + { + "rolls": { + "type": "minecraft:uniform", + "min": 2, + "max": 4 + }, + "entries": [ + { + "type": "minecraft:item", + "name": "minecraft:bone", + "weight": 10, + "functions": [ + { + "function": "minecraft:set_count", + "count": { + "type": "minecraft:uniform", + "min": 2, + "max": 6 + } + } + ] + }, + { + "type": "minecraft:item", + "name": "minecraft:string", + "weight": 10, + "functions": [ + { + "function": "minecraft:set_count", + "count": { + "type": "minecraft:uniform", + "min": 2, + "max": 6 + } + } + ] + }, + { + "type": "minecraft:item", + "name": "minecraft:coal", + "weight": 8, + "functions": [ + { + "function": "minecraft:set_count", + "count": { + "type": "minecraft:uniform", + "min": 2, + "max": 6 + } + } + ] + }, + { + "type": "minecraft:item", + "name": "minecraft:torch", + "weight": 8, + "functions": [ + { + "function": "minecraft:set_count", + "count": { + "type": "minecraft:uniform", + "min": 2, + "max": 8 + } + } + ] + }, + { + "type": "minecraft:item", + "name": "minecraft:rotten_flesh", + "weight": 8, + "functions": [ + { + "function": "minecraft:set_count", + "count": { + "type": "minecraft:uniform", + "min": 2, + "max": 6 + } + } + ] + }, + { + "type": "minecraft:item", + "name": "minecraft:arrow", + "weight": 6, + "functions": [ + { + "function": "minecraft:set_count", + "count": { + "type": "minecraft:uniform", + "min": 2, + "max": 8 + } + } + ] + } + ] + } + ], + "random_sequence": "antarchy:chests/tigers_eye_shrine/treasure" +} diff --git a/common-1.21.1/src/main/resources/data/antarchy/loot_table/chests/underside_ruins/generic.json b/common-1.21.1/src/main/resources/data/antarchy/loot_table/chests/underside_ruins/generic.json index a8a225ae..f43f4a1d 100644 --- a/common-1.21.1/src/main/resources/data/antarchy/loot_table/chests/underside_ruins/generic.json +++ b/common-1.21.1/src/main/resources/data/antarchy/loot_table/chests/underside_ruins/generic.json @@ -1,4 +1,8 @@ {"type":"minecraft:chest","pools":[{"rolls":{"type":"minecraft:uniform","min":5,"max":8},"entries":[ +{"type":"minecraft:item","name":"antarchy:floppy_disk","weight":6,"functions":[{"function":"minecraft:set_components","components":{"antarchy:floppy_disk":"antarchy:thoraxis"}}]}, +{"type":"minecraft:item","name":"antarchy:floppy_disk","weight":5,"functions":[{"function":"minecraft:set_components","components":{"antarchy:floppy_disk":"antarchy:temporal_fields"}}]}, +{"type":"minecraft:item","name":"antarchy:floppy_disk","weight":5,"functions":[{"function":"minecraft:set_components","components":{"antarchy:floppy_disk":"antarchy:bloodglass"}}]}, +{"type":"minecraft:item","name":"antarchy:floppy_disk","weight":5,"functions":[{"function":"minecraft:set_components","components":{"antarchy:floppy_disk":"antarchy:vortex"}}]}, {"type":"minecraft:item","name":"minecraft:arrow","weight":16,"functions":[{"function":"minecraft:set_count","count":{"type":"minecraft:uniform","min":10,"max":28}}]}, {"type":"minecraft:item","name":"minecraft:tipped_arrow","weight":9,"functions":[{"function":"minecraft:set_count","count":{"type":"minecraft:uniform","min":2,"max":6}},{"function":"minecraft:set_potion","id":"antarchy:inversion"}]}, {"type":"minecraft:item","name":"minecraft:iron_pickaxe","weight":10},{"type":"minecraft:item","name":"minecraft:iron_sword","weight":8}, diff --git a/common-1.21.1/src/main/resources/data/antarchy/structure/tigers_eye_shrine/tigers_eye_shrine.nbt b/common-1.21.1/src/main/resources/data/antarchy/structure/tigers_eye_shrine/tigers_eye_shrine.nbt new file mode 100644 index 00000000..dd2c9b17 Binary files /dev/null and b/common-1.21.1/src/main/resources/data/antarchy/structure/tigers_eye_shrine/tigers_eye_shrine.nbt differ diff --git a/common-1.21.1/src/main/resources/data/antarchy/tags/worldgen/biome/has_structure/tigers_eye_shrine_biomes.json b/common-1.21.1/src/main/resources/data/antarchy/tags/worldgen/biome/has_structure/tigers_eye_shrine_biomes.json new file mode 100644 index 00000000..f719dc79 --- /dev/null +++ b/common-1.21.1/src/main/resources/data/antarchy/tags/worldgen/biome/has_structure/tigers_eye_shrine_biomes.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "#antarchy:is_cavaryn" + ] +} diff --git a/common-1.21.1/src/main/resources/data/antarchy/worldgen/structure/tigers_eye_shrine.json b/common-1.21.1/src/main/resources/data/antarchy/worldgen/structure/tigers_eye_shrine.json new file mode 100644 index 00000000..847e8bfc --- /dev/null +++ b/common-1.21.1/src/main/resources/data/antarchy/worldgen/structure/tigers_eye_shrine.json @@ -0,0 +1,22 @@ +{ + "type": "integrated_api:generic_structure", + "start_pool": "antarchy:tigers_eye_shrine/tigers_eye_shrine", + "size": 1, + "terrain_height_radius_check": 4, + "allowed_terrain_height_range": 14, + "valid_biome_radius_check": 1, + "biomes": "#antarchy:has_structure/tigers_eye_shrine_biomes", + "step": "surface_structures", + "enhanced_terrain_adaptation": { + "type": "integrated_api:none" + }, + "project_start_to_heightmap": "WORLD_SURFACE_WG", + "start_height": { + "type": "minecraft:constant", + "value": { + "absolute": 0 + } + }, + "spawn_overrides": {}, + "liquid_settings": "ignore_waterlogging" +} diff --git a/common-1.21.1/src/main/resources/data/antarchy/worldgen/structure_set/tigers_eye_shrine.json b/common-1.21.1/src/main/resources/data/antarchy/worldgen/structure_set/tigers_eye_shrine.json new file mode 100644 index 00000000..5df0708e --- /dev/null +++ b/common-1.21.1/src/main/resources/data/antarchy/worldgen/structure_set/tigers_eye_shrine.json @@ -0,0 +1,14 @@ +{ + "structures": [ + { + "structure": "antarchy:tigers_eye_shrine", + "weight": 1 + } + ], + "placement": { + "salt": 149275031, + "spacing": 24, + "separation": 16, + "type": "integrated_api:advanced_random_spread" + } +} diff --git a/common-1.21.1/src/main/resources/data/antarchy/worldgen/template_pool/tigers_eye_shrine/tigers_eye_shrine.json b/common-1.21.1/src/main/resources/data/antarchy/worldgen/template_pool/tigers_eye_shrine/tigers_eye_shrine.json new file mode 100644 index 00000000..bea82d8d --- /dev/null +++ b/common-1.21.1/src/main/resources/data/antarchy/worldgen/template_pool/tigers_eye_shrine/tigers_eye_shrine.json @@ -0,0 +1,22 @@ +{ + "name": "antarchy:tigers_eye_shrine/tigers_eye_shrine", + "fallback": "minecraft:empty", + "elements": [ + { + "weight": 1, + "element": { + "location": "antarchy:tigers_eye_shrine/tigers_eye_shrine", + "processors": "antarchy:generic_processor", + "projection": "rigid", + "enhanced_terrain_adaptation": { + "type": "integrated_api:custom", + "carves": true, + "beards": true, + "kernel_size": 35, + "kernel_distance": 35 + }, + "element_type": "integrated_api:integrated_api_single_pool_element" + } + } + ] +} diff --git a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/network/AntarchyFabricClientNetworking.java b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/network/AntarchyFabricClientNetworking.java index 8151b5b8..3045beea 100644 --- a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/network/AntarchyFabricClientNetworking.java +++ b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/network/AntarchyFabricClientNetworking.java @@ -41,6 +41,7 @@ public final class AntarchyFabricClientNetworking { ClientPlayNetworking.registerGlobalReceiver(ComputerAccessResultPayload.TYPE, (payload, context) -> context.client().execute(() -> com.craisinlord.antarchy.content.client.ComputerAccessClientState.update(payload))); com.craisinlord.antarchy.content.network.ComputerNetworking.setSender(ServerPlayNetworking::send); + com.craisinlord.antarchy.content.network.AntmailNetworking.setSender(ServerPlayNetworking::send); ClientPlayNetworking.registerGlobalReceiver(GravityStatePayload.TYPE, (payload, context) -> context.client().execute(() -> AntarchyFabricNetworking.handleGravityState(context.player(), payload))); ClientPlayNetworking.registerGlobalReceiver(BloodglassStatePayload.TYPE, (payload, context) -> diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyNeoforge.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyNeoforge.java index 8597e4b7..b3f95d04 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyNeoforge.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/AntarchyNeoforge.java @@ -290,6 +290,8 @@ public class AntarchyNeoforge { AntarchyNeoforgePayloadHandlers.register(modEventBus); com.craisinlord.antarchy.content.network.ComputerNetworking.setSender(payload -> net.neoforged.neoforge.network.PacketDistributor.sendToServer(payload)); + com.craisinlord.antarchy.content.network.AntmailNetworking.setSender(payload -> + net.neoforged.neoforge.network.PacketDistributor.sendToServer((net.minecraft.network.protocol.common.custom.CustomPacketPayload) payload)); com.craisinlord.antarchy.content.network.ComputerAccessHandler.setResultSender((player, payload) -> net.neoforged.neoforge.network.PacketDistributor.sendToPlayer(player, payload)); com.craisinlord.antarchy.content.network.AntmailServerHandler.setResultSender((player, payload) ->