From 55ab3c93f1e9eb90ea9d284dd71da9fc7b7985dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=90=E6=82=A6=E8=A7=A3=E8=AF=B4?= Date: Sun, 9 Jun 2024 11:52:05 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=94=20More=20signs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tjmetro/block/base/IRailwaySign.java | 97 ++++++++- .../ziyue/tjmetro/client/ClientCache.java | 19 ++ .../tjmetro/client/RouteMapGenerator.java | 74 ++++++- .../tjmetro/mixin/RailwaySignScreenMixin.java | 70 +++++++ .../tjmetro/mixin/RenderRailwaySignMixin.java | 187 ++++++++++++++++++ .../render/RenderRailwaySignTianjin.java | 21 +- .../RenderRailwaySignTianjinDouble.java | 24 +-- .../tjmetro/render/RenderRailwaySignWall.java | 13 +- .../render/RenderRailwaySignWallDouble.java | 19 +- .../screen/RailwaySignDoubleScreen.java | 17 +- .../tjmetro/screen/RailwaySignScreen.java | 9 +- .../resources/assets/tjmetro/lang/en_us.json | 18 +- .../textures/sign/accessible_passage.png | Bin 0 -> 4146 bytes .../tjmetro/textures/sign/bound_for.png | Bin 0 -> 1108 bytes .../tjmetro/textures/sign/escalator.png | Bin 0 -> 3385 bytes .../assets/tjmetro/textures/sign/exit.png | Bin 0 -> 3316 bytes .../tjmetro/textures/sign/exit_bmt_down.png | Bin 0 -> 3582 bytes .../tjmetro/textures/sign/exit_bmt_left.png | Bin 0 -> 3467 bytes .../tjmetro/textures/sign/exit_bmt_right.png | Bin 0 -> 3468 bytes .../tjmetro/textures/sign/exit_bmt_up.png | Bin 0 -> 3507 bytes .../tjmetro/textures/sign/exit_letter.png | Bin 0 -> 2811 bytes .../tjmetro/textures/sign/fare_adjustment.png | Bin 0 -> 4409 bytes .../assets/tjmetro/textures/sign/inquiry.png | Bin 0 -> 3480 bytes .../assets/tjmetro/textures/sign/stairs.png | Bin 0 -> 3558 bytes .../assets/tjmetro/textures/sign/toilet.png | Bin 0 -> 3654 bytes common/src/main/resources/tjmetro.mixins.json | 4 +- icons/accessible_passage.svg | 21 ++ icons/escalator.svg | 18 ++ icons/exit.svg | 26 +++ icons/exit_bmt.svg | 19 ++ icons/fare_adjustment.svg | 28 +++ icons/inquiry.svg | 17 ++ icons/stairs.svg | 23 +++ icons/toilet.svg | 30 +++ 34 files changed, 699 insertions(+), 55 deletions(-) create mode 100644 common/src/main/java/ziyue/tjmetro/mixin/RailwaySignScreenMixin.java create mode 100644 common/src/main/java/ziyue/tjmetro/mixin/RenderRailwaySignMixin.java create mode 100644 common/src/main/resources/assets/tjmetro/textures/sign/accessible_passage.png create mode 100644 common/src/main/resources/assets/tjmetro/textures/sign/bound_for.png create mode 100644 common/src/main/resources/assets/tjmetro/textures/sign/escalator.png create mode 100644 common/src/main/resources/assets/tjmetro/textures/sign/exit.png create mode 100644 common/src/main/resources/assets/tjmetro/textures/sign/exit_bmt_down.png create mode 100644 common/src/main/resources/assets/tjmetro/textures/sign/exit_bmt_left.png create mode 100644 common/src/main/resources/assets/tjmetro/textures/sign/exit_bmt_right.png create mode 100644 common/src/main/resources/assets/tjmetro/textures/sign/exit_bmt_up.png create mode 100644 common/src/main/resources/assets/tjmetro/textures/sign/exit_letter.png create mode 100644 common/src/main/resources/assets/tjmetro/textures/sign/fare_adjustment.png create mode 100644 common/src/main/resources/assets/tjmetro/textures/sign/inquiry.png create mode 100644 common/src/main/resources/assets/tjmetro/textures/sign/stairs.png create mode 100644 common/src/main/resources/assets/tjmetro/textures/sign/toilet.png create mode 100644 icons/accessible_passage.svg create mode 100644 icons/escalator.svg create mode 100644 icons/exit.svg create mode 100644 icons/exit_bmt.svg create mode 100644 icons/fare_adjustment.svg create mode 100644 icons/inquiry.svg create mode 100644 icons/stairs.svg create mode 100644 icons/toilet.svg diff --git a/common/src/main/java/ziyue/tjmetro/block/base/IRailwaySign.java b/common/src/main/java/ziyue/tjmetro/block/base/IRailwaySign.java index 0cdcacd..297b8da 100644 --- a/common/src/main/java/ziyue/tjmetro/block/base/IRailwaySign.java +++ b/common/src/main/java/ziyue/tjmetro/block/base/IRailwaySign.java @@ -1,7 +1,10 @@ package ziyue.tjmetro.block.base; +import mtr.block.BlockRailwaySign; import mtr.block.IBlock; +import mtr.client.ClientData; import mtr.client.CustomResources; +import mtr.data.IGui; import mtr.mappings.Text; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -18,6 +21,10 @@ import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; import ziyue.tjmetro.Reference; +import ziyue.tjmetro.client.ClientCache; + +import java.util.Arrays; +import java.util.List; import static mtr.client.CustomResources.CUSTOM_SIGNS; import static net.minecraft.world.level.block.HorizontalDirectionalBlock.FACING; @@ -90,6 +97,53 @@ static BlockPos findEndWithDirection(Level world, BlockPos startPos, Direction d } } + static boolean signIsExit(String signId) { + List exits = Arrays.asList(BlockRailwaySign.SignType.EXIT_LETTER.toString(), BlockRailwaySign.SignType.EXIT_LETTER_FLIPPED.toString(), + SignType.EXIT_LETTER_TEXT.signId, SignType.EXIT_LETTER_TEXT_FLIPPED.signId); + return exits.contains(signId); + } + + static boolean signIsLine(String signId) { + List lines = Arrays.asList(BlockRailwaySign.SignType.LINE.toString(), BlockRailwaySign.SignType.LINE_FLIPPED.toString()); + return lines.contains(signId); + } + + static boolean signIsPlatform(String signId) { + List platforms = Arrays.asList(BlockRailwaySign.SignType.PLATFORM.toString(), BlockRailwaySign.SignType.PLATFORM_FLIPPED.toString(), + SignType.BOUND_FOR_TEXT.signId, SignType.BOUND_FOR_TEXT_FLIPPED.signId); + return platforms.contains(signId); + } + + static boolean signIsStation(String signId) { + List stations = Arrays.asList(BlockRailwaySign.SignType.STATION.toString(), BlockRailwaySign.SignType.STATION_FLIPPED.toString(), + SignType.STATION_TEXT.signId, SignType.STATION_TEXT_FLIPPED.signId); + return stations.contains(signId); + } + + static ResourceLocation getExitSignResource(String signId, String exitLetter, String exitNumber, int backgroundColor, boolean forceMTRFont) { + if (signId.equals(SignType.EXIT_LETTER_TEXT.signId) || signId.equals(SignType.EXIT_LETTER_TEXT_FLIPPED.signId)) { + return ClientCache.DATA_CACHE.getExitSignLetterTianjin(exitLetter, exitNumber, backgroundColor, forceMTRFont).resourceLocation; + } else { + if (forceMTRFont) { + return ClientData.DATA_CACHE.getExitSignLetter(exitLetter, exitNumber, backgroundColor).resourceLocation; + } else { + return ClientCache.DATA_CACHE.getExitSignLetter(exitLetter, exitNumber, backgroundColor).resourceLocation; + } + } + } + + static ResourceLocation getPlatformSignResource(String signId, long platformId, IGui.HorizontalAlignment horizontalAlignment, float paddingScale, float aspectRatio, int backgroundColor, int textColor, int transparentColor, boolean forceMTRFont) { + if (signId.equals(SignType.BOUND_FOR_TEXT.signId) || signId.equals(SignType.BOUND_FOR_TEXT_FLIPPED.signId)) { + return ClientCache.DATA_CACHE.getBoundFor(platformId, horizontalAlignment, aspectRatio, paddingScale, backgroundColor, forceMTRFont).resourceLocation; + } else { + if (forceMTRFont) { + return ClientData.DATA_CACHE.getDirectionArrow(platformId, false, false, horizontalAlignment, false, paddingScale, aspectRatio, backgroundColor, textColor, transparentColor).resourceLocation; + } else { + return ClientCache.DATA_CACHE.getDirectionArrow(platformId, false, false, horizontalAlignment, false, paddingScale, aspectRatio, backgroundColor, textColor, transparentColor).resourceLocation; + } + } + } + enum SignType { // Tianjin Rail Transit (TRT, Tianjin Metro) @@ -101,6 +155,8 @@ enum SignType TIANJIN_METRO_OLD_LOGO_TEXT_FLIPPED("tianjin_metro_old_logo", "tianjin_metro", false, true), TRAIN_TEXT("train", "train", false, false), TRAIN_TEXT_FLIPPED("train", "train", false, true), + STATION_TEXT("train", "station", false, false), + STATION_TEXT_FLIPPED("train", "station", false, true), EMERGENCY_EXIT_TEXT("emergency_exit", "emergency_exit", false, false), EMERGENCY_EXIT_TEXT_FLIPPED("emergency_exit", "emergency_exit", true, true), NO_THROUGHFARE_TEXT("no_throughfare", "no_throughfare", false, false), @@ -115,6 +171,18 @@ enum SignType ACCESSIBLE_ELEVATOR_TEXT_FLIPPED("accessible_elevator", "accessible_elevator", false, true), ACCESSIBLE_TOILET_TEXT("accessible_toilet", "accessible_toilet", false, false), ACCESSIBLE_TOILET_TEXT_FLIPPED("accessible_toilet", "accessible_toilet", false, true), + FARE_ADJUSTMENT_TEXT("fare_adjustment", "fare_adjustment", false, false), + FARE_ADJUSTMENT_TEXT_FLIPPED("fare_adjustment", "fare_adjustment", false, true), + INQUIRY_TEXT("inquiry", "inquiry", false, false), + INQUIRY_TEXT_FLIPPED("inquiry", "inquiry", false, true), + EXIT_LETTER_TEXT("exit_letter", "exit_bmt", false, false), + EXIT_LETTER_TEXT_FLIPPED("exit_letter", "exit_bmt", false, true), + BOUND_FOR_TEXT("bound_for", "bound_for", false, false), + BOUND_FOR_TEXT_FLIPPED("bound_for", "bound_for", false, true), + ACCESSIBLE_PASSAGE_TEXT("accessible_passage", "accessible_passage", false, false), + ACCESSIBLE_PASSAGE_TEXT_FLIPPED("accessible_passage", "accessible_passage", true, true), + TOILET_TEXT("toilet", "toilet", false, false), + TOILET_TEXT_FLIPPED("toilet", "toilet", false, true), TIANJIN_METRO_LOGO("tianjin_metro_logo", false), TIANJIN_METRO_MOD_LOGO("tianjin_metro_mod_logo", false), @@ -128,25 +196,46 @@ enum SignType RAILWAY_STATION("railway_station", false), ACCESSIBLE_ELEVATOR("accessible_elevator", false), ACCESSIBLE_TOILET("accessible_toilet", false), + FARE_ADJUSTMENT("fare_adjustment", false), + INQUIRY("inquiry", false), + //EXIT("exit", false), + STAIRS("stairs", false), + STAIRS_FLIPPED("stairs", true), + ACCESSIBLE_PASSAGE("accessible_passage", false), + ACCESSIBLE_PASSAGE_FLIPPED("accessible_passage", true), + ESCALATOR("escalator", false), + ESCALATOR_FLIPPED("escalator", true), + TOILET("toilet", false), // Tianjin Binhai Mass Transit (BMT, Tianjin Metro Line 9) NO_ENTRY_BMT_TEXT("no_entry_bmt", "no_entry_bmt", false, false), NO_ENTRY_BMT_TEXT_FLIPPED("no_entry_bmt", "no_entry_bmt", false, true), TO_SUBWAY_BMT_TEXT("to_subway_bmt", "to_subway_bmt", false, false), TO_SUBWAY_BMT_TEXT_FLIPPED("to_subway_bmt", "to_subway_bmt", true, true), + EXIT_BMT_UP_TEXT("exit_bmt_up", "exit_bmt", false, false), + EXIT_BMT_DOWN_TEXT("exit_bmt_down", "exit_bmt", false, false), + EXIT_BMT_UP_TEXT_FLIPPED("exit_bmt_up", "exit_bmt", false, true), + EXIT_BMT_DOWN_TEXT_FLIPPED("exit_bmt_down", "exit_bmt", false, true), + EXIT_BMT_LEFT_TEXT("exit_bmt_left", "exit_bmt", false, false), + EXIT_BMT_RIGHT_TEXT("exit_bmt_right", "exit_bmt", false, true), + TICKETS_BMT_TEXT("fare_adjustment", "tickets_bmt", false, false), + TICKETS_BMT_TEXT_FLIPPED("fare_adjustment", "tickets_bmt", false, true), NO_ENTRY_BMT("no_entry_bmt", false), TO_SUBWAY_BMT("to_subway_bmt", false), - TO_SUBWAY_BMT_FLIPPED("to_subway_bmt", true); + TO_SUBWAY_BMT_FLIPPED("to_subway_bmt", true), //BINHAI_MASS_TRANSIT("binhai_mass_transit", false), - //BINHAI_MASS_TRANSIT_FLIPPED("binhai_mass_transit", true); + //BINHAI_MASS_TRANSIT_FLIPPED("binhai_mass_transit", true), + EXIT_BMT_UP("exit_bmt_up", false), + EXIT_BMT_DOWN("exit_bmt_down", false), + EXIT_BMT_LEFT("exit_bmt_left", false), + EXIT_BMT_RIGHT("exit_bmt_right", false); public final String signId; public final CustomResources.CustomSign sign; SignType(String texture, String translation, boolean flipTexture, boolean flipCustomText, boolean hasCustomText, int backgroundColor) { - this.signId = (texture.endsWith("bmt") ? "\1tjmetro_%s_%s_%s_%s_%s_%s" : "\0tjmetro_%s_%s_%s_%s_%s_%s") // Make sure that signs will always order in front of custom signs, and BMT signs are after TRT ;) - .formatted(texture, translation, flipTexture, flipCustomText, hasCustomText, backgroundColor); + this.signId = (this.toString().contains("BMT") ? "\1_TJMETRO_%s" : "\0_TJMETRO_%s").formatted(this.toString()); // Make sure that signs will always order in front of custom signs, and BMT signs are after TRT ;) this.sign = new CustomResources.CustomSign(new ResourceLocation(Reference.MOD_ID, "textures/sign/" + texture + ".png"), flipTexture, hasCustomText ? Text.translatable("sign.tjmetro." + translation + "_cjk").append("|").append(Text.translatable("sign.tjmetro." + translation)).getString() : "", flipCustomText, true, backgroundColor); CUSTOM_SIGNS.put(signId, sign); } diff --git a/common/src/main/java/ziyue/tjmetro/client/ClientCache.java b/common/src/main/java/ziyue/tjmetro/client/ClientCache.java index 8eee1c5..76f1941 100644 --- a/common/src/main/java/ziyue/tjmetro/client/ClientCache.java +++ b/common/src/main/java/ziyue/tjmetro/client/ClientCache.java @@ -6,6 +6,7 @@ import mtr.MTR; import mtr.client.ClientCache.ColorNameTuple; import mtr.client.ClientCache.PlatformRouteDetails; +import mtr.client.ClientData; import mtr.data.*; import mtr.mappings.Utilities; import net.minecraft.client.Minecraft; @@ -149,6 +150,14 @@ public DynamicResource getExitSignLetter(String exitLetter, String exitNumber, i return getResource(String.format("tjmetro_exit_sign_letter_%s_%s", exitLetter, exitNumber), () -> RouteMapGenerator.generateExitSignLetter(exitLetter, exitNumber, backgroundColor), DefaultRenderingColor.TRANSPARENT); } + public DynamicResource getExitSignLetterTianjin(String exitLetter, String exitNumber, int backgroundColor, boolean forceMTRFont) { + return getResource(String.format("tjmetro_exit_sign_letter_tianjin_%s_%s_%s", exitLetter, exitNumber, forceMTRFont), () -> RouteMapGenerator.generateExitSignLetterTianjin(exitLetter, exitNumber, backgroundColor, forceMTRFont), DefaultRenderingColor.TRANSPARENT); + } + + public DynamicResource getBoundFor(long platformId, IGui.HorizontalAlignment horizontalAlignment, float aspectRatio, float paddingScale, int backgroundColor, boolean forceMTRFont) { + return getResource(String.format("tjmetro_bound_for_%s_%s_%s_%s_%s_%s", platformId, horizontalAlignment, aspectRatio, paddingScale, backgroundColor, forceMTRFont), () -> RouteMapGenerator.generateBoundFor(platformId, horizontalAlignment, aspectRatio, paddingScale, backgroundColor, forceMTRFont), ClientCache.DefaultRenderingColor.TRANSPARENT); + } + public DynamicResource getSignText(String string, HorizontalAlignment horizontalAlignment, float paddingScale, int backgroundColor, int textColor) { return getResource(String.format("tjmetro_sign_text_%s_%s_%s_%s_%s", string, horizontalAlignment, paddingScale, backgroundColor, textColor), () -> RouteMapGenerator.generateSignText(string, horizontalAlignment, paddingScale, backgroundColor, textColor), DefaultRenderingColor.TRANSPARENT); } @@ -224,6 +233,16 @@ protected DynamicResource getResource(String key, Supplier supplier } public Text getText(String text, int maxWidth, int maxHeight, int fontSizeCjk, int fontSize, int padding, IGui.HorizontalAlignment horizontalAlignment) { + return getText(text, maxWidth, maxHeight, fontSizeCjk, fontSize, padding, horizontalAlignment, false); + } + + public Text getText(String text, int maxWidth, int maxHeight, int fontSizeCjk, int fontSize, int padding, IGui.HorizontalAlignment horizontalAlignment, boolean forceMTRFont) { + if (forceMTRFont) { + final int[] dimensions = new int[2]; + final byte[] pixels = ClientData.DATA_CACHE.getTextPixels(text, dimensions, maxWidth, maxHeight, fontSizeCjk, fontSize, padding, horizontalAlignment); + return new ClientCache.Text(pixels, dimensions[0], dimensions[1], dimensions[0]); + } + if (maxWidth <= 0) { return new Text(new byte[0], 0, 0, 0); } diff --git a/common/src/main/java/ziyue/tjmetro/client/RouteMapGenerator.java b/common/src/main/java/ziyue/tjmetro/client/RouteMapGenerator.java index 4ec0c3f..647936e 100644 --- a/common/src/main/java/ziyue/tjmetro/client/RouteMapGenerator.java +++ b/common/src/main/java/ziyue/tjmetro/client/RouteMapGenerator.java @@ -446,7 +446,7 @@ public static NativeImage generateExitSignLetter(String exitLetter, String exitN final NativeImage nativeImage = new NativeImage(NativeImage.Format.RGBA, size, size, false); nativeImage.fillRect(0, 0, size, size, backgroundColor); drawResource(nativeImage, EXIT_RESOURCE, 0, 0, size, size, false, 0, 1, 0, true); - drawString(nativeImage, letter, size / 2 - (noNumber ? 0 : textSize / 6 - size / 32), size / 2, HorizontalAlignment.CENTER, VerticalAlignment.CENTER, 0, ARGB_WHITE, false); + drawString(nativeImage, letter, size / 2 - (noNumber ? 0 : textSize / 6 - size / 32), size / 2, HorizontalAlignment.CENTER, VerticalAlignment.CENTER, 0, ARGB_WHITE, false); if (!noNumber) { drawString(nativeImage, number, size / 2 + textSize / 3 - size / 32, size / 2 + textSize / 8, HorizontalAlignment.CENTER, VerticalAlignment.CENTER, 0, ARGB_WHITE, false); } @@ -458,6 +458,78 @@ public static NativeImage generateExitSignLetter(String exitLetter, String exitN return null; } + public static NativeImage generateExitSignLetterTianjin(String exitLetter, String exitNumber, int backgroundColor, boolean forceMTRFont) { + try { + final int size = scale / 2; + final boolean noNumber = exitNumber.isEmpty(); + final int textSize = (int) (size * 1.25F); + + final ClientCache.Text letter = ClientCache.DATA_CACHE.getText(exitLetter, noNumber ? textSize : textSize * 2 / 3, textSize, textSize, size, size, HorizontalAlignment.CENTER, forceMTRFont); + final ClientCache.Text number = noNumber ? null : ClientCache.DATA_CACHE.getText(exitNumber, textSize / 3, textSize, textSize / 2, textSize / 2, size, HorizontalAlignment.CENTER, forceMTRFont); + + final NativeImage nativeImage = new NativeImage(NativeImage.Format.RGBA, size, size, false); + nativeImage.fillRect(0, 0, size, size, backgroundColor); + drawString(nativeImage, letter, size / 2 - (noNumber ? 0 : textSize / 6 - size / 32), size / 2, HorizontalAlignment.CENTER, VerticalAlignment.CENTER, 0, ARGB_WHITE, false); + if (!noNumber) { + drawString(nativeImage, number, size / 2 + textSize / 3 - size / 32, size / 2 + textSize / 8, HorizontalAlignment.CENTER, VerticalAlignment.CENTER, 0, ARGB_WHITE, false); + } + return nativeImage; + } catch (Exception e) { + TianjinMetro.LOGGER.error(e.getMessage(), e); + } + + return null; + } + + public static NativeImage generateBoundFor(long platformId, HorizontalAlignment horizontalAlignment, float aspectRatio, float paddingScale, int backgroundColor, boolean forceMTRFont) { + try { + final int height = scale; + final int width = (int) (height * aspectRatio); + final int padding = Math.round(height * paddingScale); + final int tileSize = height - padding * 2; + + final List destinations = new ArrayList<>(); + //final List> routes = new ArrayList<>(); + final List allRoutes = getRouteStream(platformId, (route, currentStationIndex) -> destinations.add(ClientData.DATA_CACHE.getFormattedRouteDestination(route, currentStationIndex, TEMP_CIRCULAR_MARKER))); + //allRoutes.forEach( + // route -> { + // final ClientCache.Text name = ClientCache.DATA_CACHE.getText(route.name, Integer.MAX_VALUE, (int) ((fontSizeBig + fontSizeSmall) * mtr.client.ClientCache.LINE_HEIGHT_MULTIPLIER * 1.25F), (int) (fontSizeBig * 1.25F), (int) (fontSizeSmall * 1.25F), padding, horizontalAlignment, forceMTRFont); + // routes.add(new Tuple<>(name, route.color)); + // width.addAndGet(routeSquarePadding * 5 + name.width()); + // } + //); + final boolean isTerminating = destinations.isEmpty(); + final ClientCache.Text boundFor; + if (isTerminating) { + boundFor = ClientCache.DATA_CACHE.getText(IGuiExtends.mergeTranslation("gui.tjmetro.terminus_cjk", "gui.tjmetro.terminus"), width, height, tileSize * 3 / 5, tileSize * 3 / 10, padding, horizontalAlignment, forceMTRFont); + } else { + String destinationString = IGui.mergeStations(destinations); + final boolean noToString = destinationString.startsWith(TEMP_CIRCULAR_MARKER); + destinationString = destinationString.replace(TEMP_CIRCULAR_MARKER, ""); + if (!destinationString.isEmpty() && !noToString) { + destinationString = IGui.insertTranslation("sign.tjmetro.bound_for_cjk", "sign.tjmetro.bound_for", 1, destinationString); + } + boundFor = ClientCache.DATA_CACHE.getText(destinationString, width, height, tileSize * 3 / 5, tileSize * 3 / 10, padding, horizontalAlignment, forceMTRFont); + } + + final NativeImage nativeImage = new NativeImage(NativeImage.Format.RGBA, width, height, false); + nativeImage.fillRect(0, 0, width, height, invertColor(backgroundColor)); + + //routes.forEach(route -> { + // nativeImage.fillRect(currentX.get(), 0, route.getA().width(), route.getA().height(), invertColor(ARGB_BLACK | route.getB())); + // drawString(nativeImage, route.getA(), currentX.get(), height / 2, horizontalAlignment, VerticalAlignment.CENTER, 0, ARGB_WHITE, false); + // currentX.addAndGet(routeSquarePadding * 3 + route.getA().width()); + //}); + drawString(nativeImage, boundFor, 0, height / 2, horizontalAlignment, VerticalAlignment.CENTER, backgroundColor, ARGB_WHITE, false); + clearColor(nativeImage, invertColor(backgroundColor)); + + return nativeImage; + } catch (Exception e) { + TianjinMetro.LOGGER.error(e.getMessage(), e); + } + + return null; + } public static NativeImage generateSignText(String string, HorizontalAlignment horizontalAlignment, float paddingScale, int backgroundColor, int textColor) { try { diff --git a/common/src/main/java/ziyue/tjmetro/mixin/RailwaySignScreenMixin.java b/common/src/main/java/ziyue/tjmetro/mixin/RailwaySignScreenMixin.java new file mode 100644 index 0000000..da5ebf7 --- /dev/null +++ b/common/src/main/java/ziyue/tjmetro/mixin/RailwaySignScreenMixin.java @@ -0,0 +1,70 @@ +package ziyue.tjmetro.mixin; + +import mtr.data.IGui; +import mtr.data.NameColorDataBase; +import mtr.mappings.ScreenMapper; +import mtr.mappings.UtilitiesClient; +import mtr.screen.DashboardListSelectorScreen; +import mtr.screen.RailwaySignScreen; +import net.minecraft.network.chat.Component; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; +import ziyue.tjmetro.block.base.IRailwaySign; + +import java.util.List; +import java.util.Set; + +@Mixin(RailwaySignScreen.class) +public abstract class RailwaySignScreenMixin extends ScreenMapper implements IGui +{ + @Shadow(remap = false) + private int editingIndex; + + @Shadow(remap = false) + @Final + private String[] signIds; + + @Shadow(remap = false) + @Final + private List exitsForList; + + @Shadow(remap = false) + @Final + private List platformsForList; + + @Shadow(remap = false) + @Final + private List routesForList; + + @Shadow(remap = false) + @Final + private List stationsForList; + + @Shadow(remap = false) + @Final + private Set selectedIds; + + protected RailwaySignScreenMixin(Component title) { + super(title); + } + + /** + * @author + * @reason + */ + @Overwrite(remap = false) + private void setNewSignId(String newSignId) { + if (editingIndex >= 0 && editingIndex < signIds.length) { + signIds[editingIndex] = newSignId; + final boolean isExitLetter = IRailwaySign.signIsExit(newSignId); + final boolean isLine = IRailwaySign.signIsLine(newSignId); + final boolean isPlatform = IRailwaySign.signIsPlatform(newSignId); + final boolean isStation = IRailwaySign.signIsStation(newSignId); + if ((isExitLetter || isPlatform || isLine || isStation) && minecraft != null) { + UtilitiesClient.setScreen(minecraft, new DashboardListSelectorScreen(this, isExitLetter ? exitsForList : isPlatform ? platformsForList : isLine ? routesForList : stationsForList, selectedIds, false, false)); + } + } + } +} diff --git a/common/src/main/java/ziyue/tjmetro/mixin/RenderRailwaySignMixin.java b/common/src/main/java/ziyue/tjmetro/mixin/RenderRailwaySignMixin.java new file mode 100644 index 0000000..9736565 --- /dev/null +++ b/common/src/main/java/ziyue/tjmetro/mixin/RenderRailwaySignMixin.java @@ -0,0 +1,187 @@ +package ziyue.tjmetro.mixin; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.Tesselator; +import mtr.block.BlockRailwaySign; +import mtr.block.IBlock; +import mtr.client.ClientData; +import mtr.client.CustomResources; +import mtr.client.IDrawing; +import mtr.data.IGui; +import mtr.data.Platform; +import mtr.data.RailwayData; +import mtr.data.Station; +import mtr.mappings.BlockEntityRendererMapper; +import mtr.render.RenderRailwaySign; +import mtr.render.RenderTrains; +import mtr.render.StoredMatrixTransformations; +import net.minecraft.client.gui.Font; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; +import ziyue.tjmetro.block.base.IRailwaySign; +import ziyue.tjmetro.client.ClientCache; + +import java.util.*; +import java.util.stream.Collectors; + +import static mtr.render.RenderRailwaySign.getSign; + +@Mixin(RenderRailwaySign.class) +public abstract class RenderRailwaySignMixin extends BlockEntityRendererMapper implements IBlock, IGui, IDrawing +{ + @Shadow(remap = false) + private static void renderCustomText(String signText, StoredMatrixTransformations storedMatrixTransformations, Direction facing, float size, float start, boolean flipCustomText, float maxWidth, int backgroundColor) { + } + + public RenderRailwaySignMixin(BlockEntityRenderDispatcher dispatcher) { + super(dispatcher); + } + + /** + * @author + * @reason + */ + @Overwrite(remap = false) + public static void drawSign(PoseStack matrices, MultiBufferSource vertexConsumers, StoredMatrixTransformations storedMatrixTransformations, Font textRenderer, BlockPos pos, String signId, float x, float y, float size, float maxWidthLeft, float maxWidthRight, Set selectedIds, Direction facing, int backgroundColor, RenderRailwaySign.DrawTexture drawTexture) { + if (RenderTrains.shouldNotRender(pos, RenderTrains.maxTrainRenderDistance, facing)) return; + + final CustomResources.CustomSign sign = getSign(signId); + if (sign == null) return; + + final float signSize = (sign.small ? BlockRailwaySign.SMALL_SIGN_PERCENTAGE : 1) * size; + final float margin = (size - signSize) / 2; + + final boolean hasCustomText = sign.hasCustomText(); + final boolean flipCustomText = sign.flipCustomText; + final boolean flipTexture = sign.flipTexture; + final boolean isExit = IRailwaySign.signIsExit(signId); + final boolean isLine = IRailwaySign.signIsLine(signId); + final boolean isPlatform = IRailwaySign.signIsPlatform(signId); + final boolean isStation = IRailwaySign.signIsStation(signId); + + final MultiBufferSource.BufferSource immediate = RenderTrains.shouldNotRender(pos, RenderTrains.maxTrainRenderDistance / 2, null) ? null : MultiBufferSource.immediate(Tesselator.getInstance().getBuilder()); + + if (vertexConsumers != null && isExit) { + final Station station = RailwayData.getStation(ClientData.STATIONS, ClientData.DATA_CACHE, pos); + if (station == null) return; + + final Map> exits = station.getGeneratedExits(); + final List selectedExitsSorted = selectedIds.stream().map(Station::deserializeExit).filter(exits::containsKey).sorted(String::compareTo).toList(); + + matrices.pushPose(); + matrices.translate(x + margin + (flipCustomText ? signSize : 0), y + margin, 0); + final float maxWidth = ((flipCustomText ? maxWidthLeft : maxWidthRight) + 1) * size - margin * 2; + final float exitWidth = signSize * selectedExitsSorted.size(); + matrices.scale(Math.min(1, maxWidth / exitWidth), 1, 1); + + for (int i = 0; i < selectedExitsSorted.size(); i++) { + final String selectedExit = selectedExitsSorted.get(flipCustomText ? selectedExitsSorted.size() - i - 1 : i); + final float offset = (flipCustomText ? -1 : 1) * signSize * i - (flipCustomText ? signSize : 0); + + RenderTrains.scheduleRender(IRailwaySign.getExitSignResource(signId, selectedExit.substring(0, 1), selectedExit.substring(1), backgroundColor, true), true, RenderTrains.QueuedRenderLayer.LIGHT_TRANSLUCENT, (matricesNew, vertexConsumer) -> { + storedMatrixTransformations.transform(matricesNew); + matricesNew.translate(x + margin + (flipCustomText ? signSize : 0), y + margin, 0); + matricesNew.scale(Math.min(1, maxWidth / exitWidth), 1, 1); + IDrawing.drawTexture(matricesNew, vertexConsumer, offset, 0, signSize, signSize, facing, MAX_LIGHT_GLOWING); + matricesNew.popPose(); + }); + + if (maxWidth > exitWidth && selectedExitsSorted.size() == 1 && !exits.get(selectedExit).isEmpty()) { + renderCustomText(exits.get(selectedExit).get(0), storedMatrixTransformations, facing, size, flipCustomText ? x : x + size, flipCustomText, maxWidth - exitWidth - margin * 2, backgroundColor); + } + } + + matrices.popPose(); + } else if (vertexConsumers != null && isLine) { + final Station station = RailwayData.getStation(ClientData.STATIONS, ClientData.DATA_CACHE, pos); + if (station == null) return; + + final Map routesInStation = ClientData.DATA_CACHE.getAllRoutesIncludingConnectingStations(station); + final List selectedIdsSorted = selectedIds.stream().filter(selectedId -> RailwayData.isBetween(selectedId, Integer.MIN_VALUE, Integer.MAX_VALUE)).map(Math::toIntExact).filter(routesInStation::containsKey).map(routesInStation::get).sorted(Comparator.comparingInt(route -> route.color)).toList(); + + final float maxWidth = Math.max(0, ((flipCustomText ? maxWidthLeft : maxWidthRight) + 1) * size - margin * 2); + final float height = size - margin * 2; + final List resourceLocationDataList = new ArrayList<>(); + float totalTextWidth = 0; + for (final mtr.client.ClientCache.ColorNameTuple route : selectedIdsSorted) { + final mtr.client.ClientCache.DynamicResource resourceLocationData = ClientData.DATA_CACHE.getRouteSquare(route.color, route.name, flipCustomText ? HorizontalAlignment.RIGHT : HorizontalAlignment.LEFT); + resourceLocationDataList.add(resourceLocationData); + totalTextWidth += height * resourceLocationData.width / resourceLocationData.height + margin / 2F; + } + + final StoredMatrixTransformations storedMatrixTransformations2 = storedMatrixTransformations.copy(); + storedMatrixTransformations2.add(matricesNew -> matricesNew.translate(flipCustomText ? x + size - margin : x + margin, 0, 0)); + + if (totalTextWidth > margin / 2F) { + totalTextWidth -= margin / 2F; + } + if (totalTextWidth > maxWidth) { + final float finalTotalTextWidth = totalTextWidth; + storedMatrixTransformations2.add(matricesNew -> matricesNew.scale(maxWidth / finalTotalTextWidth, 1, 1)); + } + + float xOffset = 0; + for (final mtr.client.ClientCache.DynamicResource resourceLocationData : resourceLocationDataList) { + final float width = height * resourceLocationData.width / resourceLocationData.height; + final float finalXOffset = xOffset; + RenderTrains.scheduleRender(resourceLocationData.resourceLocation, true, RenderTrains.QueuedRenderLayer.LIGHT, (matricesNew, vertexConsumer) -> { + storedMatrixTransformations2.transform(matricesNew); + IDrawing.drawTexture(matricesNew, vertexConsumer, flipCustomText ? -finalXOffset - width : finalXOffset, margin, width, height, Direction.UP, MAX_LIGHT_GLOWING); + matricesNew.popPose(); + }); + xOffset += width + margin / 2F; + } + } else if (vertexConsumers != null && isPlatform) { + final Station station = RailwayData.getStation(ClientData.STATIONS, ClientData.DATA_CACHE, pos); + if (station == null) return; + + final Map platformPositions = ClientData.DATA_CACHE.requestStationIdToPlatforms(station.id); + if (platformPositions != null) { + final List selectedIdsSorted = selectedIds.stream().filter(platformPositions::containsKey).sorted(Comparator.comparing(platformPositions::get)).toList(); + final int selectedCount = selectedIdsSorted.size(); + + final float extraMargin = margin - margin / selectedCount; + final float height = (size - extraMargin * 2) / selectedCount; + for (int i = 0; i < selectedIdsSorted.size(); i++) { + final float topOffset = i * height + extraMargin; + final float bottomOffset = (i + 1) * height + extraMargin; + final float left = flipCustomText ? x - maxWidthLeft * size : x + margin; + final float right = flipCustomText ? x + size - margin : x + (maxWidthRight + 1) * size; + RenderTrains.scheduleRender(IRailwaySign.getPlatformSignResource(signId, selectedIdsSorted.get(i), flipCustomText ? HorizontalAlignment.RIGHT : HorizontalAlignment.LEFT, margin / size, (right - left) / (bottomOffset - topOffset), backgroundColor, ARGB_WHITE, backgroundColor, true), true, RenderTrains.QueuedRenderLayer.LIGHT_TRANSLUCENT, (matricesNew, vertexConsumer) -> { + storedMatrixTransformations.transform(matricesNew); + IDrawing.drawTexture(matricesNew, vertexConsumer, left, topOffset, 0, right, bottomOffset, 0, 0, 0, 1, 1, facing, -1, MAX_LIGHT_GLOWING); + matricesNew.popPose(); + }); + } + } + } else { + drawTexture.drawTexture(sign.textureId, x + margin, y + margin, signSize, flipTexture); + + if (hasCustomText) { + final float fixedMargin = size * (1 - BlockRailwaySign.SMALL_SIGN_PERCENTAGE) / 2; + final boolean isSmall = sign.small; + final float maxWidth = Math.max(0, (flipCustomText ? maxWidthLeft : maxWidthRight) * size - fixedMargin * (isSmall ? 1 : 2)); + final float start = flipCustomText ? x - (isSmall ? 0 : fixedMargin) : x + size + (isSmall ? 0 : fixedMargin); + if (vertexConsumers == null) { + IDrawing.drawStringWithFont(matrices, textRenderer, immediate, isExit || isLine ? "..." : sign.customText, flipCustomText ? HorizontalAlignment.RIGHT : HorizontalAlignment.LEFT, VerticalAlignment.TOP, start, y + fixedMargin, maxWidth, size - fixedMargin * 2, 0.01F, ARGB_WHITE, false, MAX_LIGHT_GLOWING, null); + } else { + final String signText; + if (isStation) { + signText = IGui.mergeStations(selectedIds.stream().filter(ClientData.DATA_CACHE.stationIdMap::containsKey).sorted(Long::compareTo).map(stationId -> IGui.insertTranslation("gui.mtr.station_cjk", "gui.mtr.station", 1, ClientData.DATA_CACHE.stationIdMap.get(stationId).name)).collect(Collectors.toList())); + } else { + signText = sign.customText; + } + renderCustomText(signText, storedMatrixTransformations, facing, size, start, flipCustomText, maxWidth, backgroundColor); + } + } + } + + if (immediate != null) immediate.endBatch(); + } +} diff --git a/common/src/main/java/ziyue/tjmetro/render/RenderRailwaySignTianjin.java b/common/src/main/java/ziyue/tjmetro/render/RenderRailwaySignTianjin.java index f31bdbd..27404be 100644 --- a/common/src/main/java/ziyue/tjmetro/render/RenderRailwaySignTianjin.java +++ b/common/src/main/java/ziyue/tjmetro/render/RenderRailwaySignTianjin.java @@ -28,6 +28,7 @@ import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.block.state.BlockState; import ziyue.tjmetro.block.BlockRailwaySignTianjin; +import ziyue.tjmetro.block.base.IRailwaySign; import ziyue.tjmetro.client.ClientCache; import java.util.*; @@ -60,7 +61,7 @@ public void render(T entity, float tickDelta, PoseStack matrices, MultiBufferSou final Direction facing = IBlock.getStatePropertySafe(state, BlockStationNameBase.FACING); final String[] signIds = entity.getSignIds(); - int backgroundColor = 0x0B0B0B; + int backgroundColor = 0; for (final String signId : signIds) { if (signId != null) { final CustomResources.CustomSign sign = getSign(signId); @@ -123,10 +124,10 @@ public static void drawSign(PoseStack matrices, MultiBufferSource vertexConsumer final boolean hasCustomText = sign.hasCustomText(); final boolean flipCustomText = sign.flipCustomText; final boolean flipTexture = sign.flipTexture; - final boolean isExit = signId.equals(BlockRailwaySign.SignType.EXIT_LETTER.toString()) || signId.equals(BlockRailwaySign.SignType.EXIT_LETTER_FLIPPED.toString()); - final boolean isLine = signId.equals(BlockRailwaySign.SignType.LINE.toString()) || signId.equals(BlockRailwaySign.SignType.LINE_FLIPPED.toString()); - final boolean isPlatform = signId.equals(BlockRailwaySign.SignType.PLATFORM.toString()) || signId.equals(BlockRailwaySign.SignType.PLATFORM_FLIPPED.toString()); - final boolean isStation = signId.equals(BlockRailwaySign.SignType.STATION.toString()) || signId.equals(BlockRailwaySign.SignType.STATION_FLIPPED.toString()); + final boolean isExit = IRailwaySign.signIsExit(signId); + final boolean isLine = IRailwaySign.signIsLine(signId); + final boolean isPlatform = IRailwaySign.signIsPlatform(signId); + final boolean isStation = IRailwaySign.signIsStation(signId); final MultiBufferSource.BufferSource immediate = RenderTrains.shouldNotRender(pos, RenderTrains.maxTrainRenderDistance / 2, null) ? null : MultiBufferSource.immediate(Tesselator.getInstance().getBuilder()); @@ -147,7 +148,7 @@ public static void drawSign(PoseStack matrices, MultiBufferSource vertexConsumer final String selectedExit = selectedExitsSorted.get(flipCustomText ? selectedExitsSorted.size() - i - 1 : i); final float offset = (flipCustomText ? -1 : 1) * signSize * i - (flipCustomText ? signSize : 0); - RenderTrains.scheduleRender(ClientCache.DATA_CACHE.getExitSignLetter(selectedExit.substring(0, 1), selectedExit.substring(1), backgroundColor).resourceLocation, true, RenderTrains.QueuedRenderLayer.LIGHT_TRANSLUCENT, (matricesNew, vertexConsumer) -> { + RenderTrains.scheduleRender(IRailwaySign.getExitSignResource(signId, selectedExit.substring(0, 1), selectedExit.substring(1), backgroundColor, false), true, RenderTrains.QueuedRenderLayer.LIGHT_TRANSLUCENT, (matricesNew, vertexConsumer) -> { storedMatrixTransformations.transform(matricesNew); matricesNew.translate(x + margin + (flipCustomText ? signSize : 0), y + margin, 0); matricesNew.scale(Math.min(1, maxWidth / exitWidth), 1, 1); @@ -170,10 +171,10 @@ public static void drawSign(PoseStack matrices, MultiBufferSource vertexConsumer final float maxWidth = Math.max(0, ((flipCustomText ? maxWidthLeft : maxWidthRight) + 1) * size - margin * 2); final float height = size - margin * 2; - final List resourceLocationDataList = new ArrayList<>(); + final List resourceLocationDataList = new ArrayList<>(); float totalTextWidth = 0; for (final mtr.client.ClientCache.ColorNameTuple route : selectedIdsSorted) { - final mtr.client.ClientCache.DynamicResource resourceLocationData = ClientData.DATA_CACHE.getRouteSquare(route.color, route.name, flipCustomText ? HorizontalAlignment.RIGHT : HorizontalAlignment.LEFT); + final ClientCache.DynamicResource resourceLocationData = ClientCache.DATA_CACHE.getRouteSquare(route.color, route.name, flipCustomText ? HorizontalAlignment.RIGHT : HorizontalAlignment.LEFT); resourceLocationDataList.add(resourceLocationData); totalTextWidth += height * resourceLocationData.width / resourceLocationData.height + margin / 2F; } @@ -190,7 +191,7 @@ public static void drawSign(PoseStack matrices, MultiBufferSource vertexConsumer } float xOffset = 0; - for (final mtr.client.ClientCache.DynamicResource resourceLocationData : resourceLocationDataList) { + for (final ClientCache.DynamicResource resourceLocationData : resourceLocationDataList) { final float width = height * resourceLocationData.width / resourceLocationData.height; final float finalXOffset = xOffset; RenderTrains.scheduleRender(resourceLocationData.resourceLocation, true, RenderTrains.QueuedRenderLayer.LIGHT, (matricesNew, vertexConsumer) -> { @@ -216,7 +217,7 @@ public static void drawSign(PoseStack matrices, MultiBufferSource vertexConsumer final float bottomOffset = (i + 1) * height + extraMargin; final float left = flipCustomText ? x - maxWidthLeft * size : x + margin; final float right = flipCustomText ? x + size - margin : x + (maxWidthRight + 1) * size; - RenderTrains.scheduleRender(ClientCache.DATA_CACHE.getDirectionArrow(selectedIdsSorted.get(i), false, false, flipCustomText ? HorizontalAlignment.RIGHT : HorizontalAlignment.LEFT, false, margin / size, (right - left) / (bottomOffset - topOffset), backgroundColor, ARGB_WHITE, backgroundColor).resourceLocation, true, RenderTrains.QueuedRenderLayer.LIGHT_TRANSLUCENT, (matricesNew, vertexConsumer) -> { + RenderTrains.scheduleRender(IRailwaySign.getPlatformSignResource(signId, selectedIdsSorted.get(i), flipCustomText ? HorizontalAlignment.RIGHT : HorizontalAlignment.LEFT, margin / size, (right - left) / (bottomOffset - topOffset), backgroundColor, ARGB_WHITE, backgroundColor, false), true, RenderTrains.QueuedRenderLayer.LIGHT_TRANSLUCENT, (matricesNew, vertexConsumer) -> { storedMatrixTransformations.transform(matricesNew); IDrawing.drawTexture(matricesNew, vertexConsumer, left, topOffset, 0, right, bottomOffset, 0, 0, 0, 1, 1, facing, -1, MAX_LIGHT_GLOWING); matricesNew.popPose(); diff --git a/common/src/main/java/ziyue/tjmetro/render/RenderRailwaySignTianjinDouble.java b/common/src/main/java/ziyue/tjmetro/render/RenderRailwaySignTianjinDouble.java index c6f6b40..a6acd44 100644 --- a/common/src/main/java/ziyue/tjmetro/render/RenderRailwaySignTianjinDouble.java +++ b/common/src/main/java/ziyue/tjmetro/render/RenderRailwaySignTianjinDouble.java @@ -28,7 +28,7 @@ import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.block.state.BlockState; import ziyue.tjmetro.block.BlockRailwaySignTianjinDouble; -import ziyue.tjmetro.block.BlockRailwaySignWallDouble; +import ziyue.tjmetro.block.base.IRailwaySign; import ziyue.tjmetro.client.ClientCache; import java.util.*; @@ -63,7 +63,7 @@ public void render(T entity, float tickDelta, PoseStack matrices, MultiBufferSou final Direction facing = IBlock.getStatePropertySafe(state, BlockStationNameBase.FACING); final String[][] signIds = entity.getSignIds(); - int[] backgroundColor = { 0x0B0B0B, 0x0B0B0B }; + int[] backgroundColor = { 0, 0 }; for (int i = 0; i < 2; i++) { for (final String signId : signIds[i]) { if (signId != null) { @@ -131,10 +131,10 @@ public static void drawSign(PoseStack matrices, MultiBufferSource vertexConsumer final boolean hasCustomText = sign.hasCustomText(); final boolean flipCustomText = sign.flipCustomText; final boolean flipTexture = sign.flipTexture; - final boolean isExit = signId.equals(BlockRailwaySign.SignType.EXIT_LETTER.toString()) || signId.equals(BlockRailwaySign.SignType.EXIT_LETTER_FLIPPED.toString()); - final boolean isLine = signId.equals(BlockRailwaySign.SignType.LINE.toString()) || signId.equals(BlockRailwaySign.SignType.LINE_FLIPPED.toString()); - final boolean isPlatform = signId.equals(BlockRailwaySign.SignType.PLATFORM.toString()) || signId.equals(BlockRailwaySign.SignType.PLATFORM_FLIPPED.toString()); - final boolean isStation = signId.equals(BlockRailwaySign.SignType.STATION.toString()) || signId.equals(BlockRailwaySign.SignType.STATION_FLIPPED.toString()); + final boolean isExit = IRailwaySign.signIsExit(signId); + final boolean isLine = IRailwaySign.signIsLine(signId); + final boolean isPlatform = IRailwaySign.signIsPlatform(signId); + final boolean isStation = IRailwaySign.signIsStation(signId); final MultiBufferSource.BufferSource immediate = RenderTrains.shouldNotRender(pos, RenderTrains.maxTrainRenderDistance / 2, null) ? null : MultiBufferSource.immediate(Tesselator.getInstance().getBuilder()); @@ -155,11 +155,11 @@ public static void drawSign(PoseStack matrices, MultiBufferSource vertexConsumer final String selectedExit = selectedExitsSorted.get(flipCustomText ? selectedExitsSorted.size() - i - 1 : i); final float offset = (flipCustomText ? -1 : 1) * signSize * i - (flipCustomText ? signSize : 0); - RenderTrains.scheduleRender(ClientCache.DATA_CACHE.getExitSignLetter(selectedExit.substring(0, 1), selectedExit.substring(1), backgroundColor).resourceLocation, true, RenderTrains.QueuedRenderLayer.LIGHT_TRANSLUCENT, (matricesNew, vertexConsumer) -> { + RenderTrains.scheduleRender(IRailwaySign.getExitSignResource(signId, selectedExit.substring(0, 1), selectedExit.substring(1), backgroundColor, false), true, RenderTrains.QueuedRenderLayer.LIGHT_TRANSLUCENT, (matricesNew, vertexConsumer) -> { storedMatrixTransformations.transform(matricesNew); - matricesNew.translate(x + margin + (flipCustomText ? signSize : 0), y + margin, 0); + matricesNew.translate(x + margin + (flipCustomText ? signSize : 0), margin, 0); matricesNew.scale(Math.min(1, maxWidth / exitWidth), 1, 1); - IDrawing.drawTexture(matricesNew, vertexConsumer, offset, 0, signSize, signSize, facing, MAX_LIGHT_GLOWING); + IDrawing.drawTexture(matricesNew, vertexConsumer, offset, y, signSize, signSize, facing, MAX_LIGHT_GLOWING); matricesNew.popPose(); }); @@ -203,7 +203,7 @@ public static void drawSign(PoseStack matrices, MultiBufferSource vertexConsumer final float finalXOffset = xOffset; RenderTrains.scheduleRender(resourceLocationData.resourceLocation, true, RenderTrains.QueuedRenderLayer.LIGHT, (matricesNew, vertexConsumer) -> { storedMatrixTransformations2.transform(matricesNew); - IDrawing.drawTexture(matricesNew, vertexConsumer, flipCustomText ? -finalXOffset - width : finalXOffset, margin, width, height, Direction.UP, MAX_LIGHT_GLOWING); + IDrawing.drawTexture(matricesNew, vertexConsumer, flipCustomText ? -finalXOffset - width : finalXOffset, margin + y, width, height, Direction.UP, MAX_LIGHT_GLOWING); matricesNew.popPose(); }); xOffset += width + margin / 2F; @@ -224,9 +224,9 @@ public static void drawSign(PoseStack matrices, MultiBufferSource vertexConsumer final float bottomOffset = (i + 1) * height + extraMargin; final float left = flipCustomText ? x - maxWidthLeft * size : x + margin; final float right = flipCustomText ? x + size - margin : x + (maxWidthRight + 1) * size; - RenderTrains.scheduleRender(ClientCache.DATA_CACHE.getDirectionArrow(selectedIdsSorted.get(i), false, false, flipCustomText ? HorizontalAlignment.RIGHT : HorizontalAlignment.LEFT, false, margin / size, (right - left) / (bottomOffset - topOffset), backgroundColor, ARGB_WHITE, backgroundColor).resourceLocation, true, RenderTrains.QueuedRenderLayer.LIGHT_TRANSLUCENT, (matricesNew, vertexConsumer) -> { + RenderTrains.scheduleRender(IRailwaySign.getPlatformSignResource(signId, selectedIdsSorted.get(i), flipCustomText ? HorizontalAlignment.RIGHT : HorizontalAlignment.LEFT, margin / size, (right - left) / (bottomOffset - topOffset), backgroundColor, ARGB_WHITE, backgroundColor, false), true, RenderTrains.QueuedRenderLayer.LIGHT_TRANSLUCENT, (matricesNew, vertexConsumer) -> { storedMatrixTransformations.transform(matricesNew); - IDrawing.drawTexture(matricesNew, vertexConsumer, left, topOffset, 0, right, bottomOffset, 0, 0, 0, 1, 1, facing, -1, MAX_LIGHT_GLOWING); + IDrawing.drawTexture(matricesNew, vertexConsumer, left, topOffset + y, 0, right, bottomOffset, 0, 0, 0, 1, 1, facing, -1, MAX_LIGHT_GLOWING); matricesNew.popPose(); }); } diff --git a/common/src/main/java/ziyue/tjmetro/render/RenderRailwaySignWall.java b/common/src/main/java/ziyue/tjmetro/render/RenderRailwaySignWall.java index 4024430..09105a1 100644 --- a/common/src/main/java/ziyue/tjmetro/render/RenderRailwaySignWall.java +++ b/common/src/main/java/ziyue/tjmetro/render/RenderRailwaySignWall.java @@ -30,6 +30,7 @@ import net.minecraft.world.level.block.state.BlockState; import ziyue.tjmetro.block.BlockRailwaySignWall; import ziyue.tjmetro.block.BlockRailwaySignWallBig; +import ziyue.tjmetro.block.base.IRailwaySign; import java.util.*; import java.util.stream.Collectors; @@ -130,10 +131,10 @@ public static void drawSign(PoseStack matrices, MultiBufferSource vertexConsumer final boolean hasCustomText = sign.hasCustomText(); final boolean flipCustomText = sign.flipCustomText; final boolean flipTexture = sign.flipTexture; - final boolean isExit = signId.equals(BlockRailwaySign.SignType.EXIT_LETTER.toString()) || signId.equals(BlockRailwaySign.SignType.EXIT_LETTER_FLIPPED.toString()); - final boolean isLine = signId.equals(BlockRailwaySign.SignType.LINE.toString()) || signId.equals(BlockRailwaySign.SignType.LINE_FLIPPED.toString()); - final boolean isPlatform = signId.equals(BlockRailwaySign.SignType.PLATFORM.toString()) || signId.equals(BlockRailwaySign.SignType.PLATFORM_FLIPPED.toString()); - final boolean isStation = signId.equals(BlockRailwaySign.SignType.STATION.toString()) || signId.equals(BlockRailwaySign.SignType.STATION_FLIPPED.toString()); + final boolean isExit = IRailwaySign.signIsExit(signId); + final boolean isLine = IRailwaySign.signIsLine(signId); + final boolean isPlatform = IRailwaySign.signIsPlatform(signId); + final boolean isStation = IRailwaySign.signIsStation(signId); final MultiBufferSource.BufferSource immediate = RenderTrains.shouldNotRender(pos, RenderTrains.maxTrainRenderDistance / 2, null) ? null : MultiBufferSource.immediate(Tesselator.getInstance().getBuilder()); @@ -154,7 +155,7 @@ public static void drawSign(PoseStack matrices, MultiBufferSource vertexConsumer final String selectedExit = selectedExitsSorted.get(flipCustomText ? selectedExitsSorted.size() - i - 1 : i); final float offset = (flipCustomText ? -1 : 1) * signSize * i - (flipCustomText ? signSize : 0); - RenderTrains.scheduleRender(ClientData.DATA_CACHE.getExitSignLetter(selectedExit.substring(0, 1), selectedExit.substring(1), backgroundColor).resourceLocation, true, RenderTrains.QueuedRenderLayer.LIGHT_TRANSLUCENT, (matricesNew, vertexConsumer) -> { + RenderTrains.scheduleRender(IRailwaySign.getExitSignResource(signId, selectedExit.substring(0, 1), selectedExit.substring(1), backgroundColor, true), true, RenderTrains.QueuedRenderLayer.LIGHT_TRANSLUCENT, (matricesNew, vertexConsumer) -> { storedMatrixTransformations.transform(matricesNew); matricesNew.translate(x + margin + (flipCustomText ? signSize : 0), y + margin, 0); matricesNew.scale(Math.min(1, maxWidth / exitWidth), 1, 1); @@ -223,7 +224,7 @@ public static void drawSign(PoseStack matrices, MultiBufferSource vertexConsumer final float bottomOffset = (i + 1) * height + extraMargin; final float left = flipCustomText ? x - maxWidthLeft * size : x + margin; final float right = flipCustomText ? x + size - margin : x + (maxWidthRight + 1) * size; - RenderTrains.scheduleRender(ClientData.DATA_CACHE.getDirectionArrow(selectedIdsSorted.get(i), false, false, flipCustomText ? HorizontalAlignment.RIGHT : HorizontalAlignment.LEFT, false, margin / size, (right - left) / (bottomOffset - topOffset), backgroundColor, ARGB_WHITE, backgroundColor).resourceLocation, true, RenderTrains.QueuedRenderLayer.LIGHT_TRANSLUCENT, (matricesNew, vertexConsumer) -> { + RenderTrains.scheduleRender(IRailwaySign.getPlatformSignResource(signId, selectedIdsSorted.get(i), flipCustomText ? HorizontalAlignment.RIGHT : HorizontalAlignment.LEFT, margin / size, (right - left) / (bottomOffset - topOffset), backgroundColor, ARGB_WHITE, backgroundColor, true), true, RenderTrains.QueuedRenderLayer.LIGHT_TRANSLUCENT, (matricesNew, vertexConsumer) -> { storedMatrixTransformations.transform(matricesNew); IDrawing.drawTexture(matricesNew, vertexConsumer, left, topOffset, 0, right, bottomOffset, 0, 0, 0, 1, 1, facing, -1, MAX_LIGHT_GLOWING); matricesNew.popPose(); diff --git a/common/src/main/java/ziyue/tjmetro/render/RenderRailwaySignWallDouble.java b/common/src/main/java/ziyue/tjmetro/render/RenderRailwaySignWallDouble.java index 4c91ddc..212bd6b 100644 --- a/common/src/main/java/ziyue/tjmetro/render/RenderRailwaySignWallDouble.java +++ b/common/src/main/java/ziyue/tjmetro/render/RenderRailwaySignWallDouble.java @@ -29,6 +29,7 @@ import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.block.state.BlockState; import ziyue.tjmetro.block.BlockRailwaySignWallDouble; +import ziyue.tjmetro.block.base.IRailwaySign; import java.util.*; import java.util.stream.Collectors; @@ -130,10 +131,10 @@ public static void drawSign(PoseStack matrices, MultiBufferSource vertexConsumer final boolean hasCustomText = sign.hasCustomText(); final boolean flipCustomText = sign.flipCustomText; final boolean flipTexture = sign.flipTexture; - final boolean isExit = signId.equals(BlockRailwaySign.SignType.EXIT_LETTER.toString()) || signId.equals(BlockRailwaySign.SignType.EXIT_LETTER_FLIPPED.toString()); - final boolean isLine = signId.equals(BlockRailwaySign.SignType.LINE.toString()) || signId.equals(BlockRailwaySign.SignType.LINE_FLIPPED.toString()); - final boolean isPlatform = signId.equals(BlockRailwaySign.SignType.PLATFORM.toString()) || signId.equals(BlockRailwaySign.SignType.PLATFORM_FLIPPED.toString()); - final boolean isStation = signId.equals(BlockRailwaySign.SignType.STATION.toString()) || signId.equals(BlockRailwaySign.SignType.STATION_FLIPPED.toString()); + final boolean isExit = IRailwaySign.signIsExit(signId); + final boolean isLine = IRailwaySign.signIsLine(signId); + final boolean isPlatform = IRailwaySign.signIsPlatform(signId); + final boolean isStation = IRailwaySign.signIsStation(signId); final MultiBufferSource.BufferSource immediate = RenderTrains.shouldNotRender(pos, RenderTrains.maxTrainRenderDistance / 2, null) ? null : MultiBufferSource.immediate(Tesselator.getInstance().getBuilder()); @@ -154,11 +155,11 @@ public static void drawSign(PoseStack matrices, MultiBufferSource vertexConsumer final String selectedExit = selectedExitsSorted.get(flipCustomText ? selectedExitsSorted.size() - i - 1 : i); final float offset = (flipCustomText ? -1 : 1) * signSize * i - (flipCustomText ? signSize : 0); - RenderTrains.scheduleRender(ClientData.DATA_CACHE.getExitSignLetter(selectedExit.substring(0, 1), selectedExit.substring(1), backgroundColor).resourceLocation, true, RenderTrains.QueuedRenderLayer.LIGHT_TRANSLUCENT, (matricesNew, vertexConsumer) -> { + RenderTrains.scheduleRender(IRailwaySign.getExitSignResource(signId, selectedExit.substring(0, 1), selectedExit.substring(1), backgroundColor, true), true, RenderTrains.QueuedRenderLayer.LIGHT_TRANSLUCENT, (matricesNew, vertexConsumer) -> { storedMatrixTransformations.transform(matricesNew); matricesNew.translate(x + margin + (flipCustomText ? signSize : 0), y + margin, 0); matricesNew.scale(Math.min(1, maxWidth / exitWidth), 1, 1); - IDrawing.drawTexture(matricesNew, vertexConsumer, offset, 0, signSize, signSize, facing, MAX_LIGHT_GLOWING); + IDrawing.drawTexture(matricesNew, vertexConsumer, offset, y, signSize, signSize, facing, MAX_LIGHT_GLOWING); matricesNew.popPose(); }); @@ -202,7 +203,7 @@ public static void drawSign(PoseStack matrices, MultiBufferSource vertexConsumer final float finalXOffset = xOffset; RenderTrains.scheduleRender(resourceLocationData.resourceLocation, true, RenderTrains.QueuedRenderLayer.LIGHT, (matricesNew, vertexConsumer) -> { storedMatrixTransformations2.transform(matricesNew); - IDrawing.drawTexture(matricesNew, vertexConsumer, flipCustomText ? -finalXOffset - width : finalXOffset, margin, width, height, Direction.UP, MAX_LIGHT_GLOWING); + IDrawing.drawTexture(matricesNew, vertexConsumer, flipCustomText ? -finalXOffset - width : finalXOffset, margin + y, width, height, Direction.UP, MAX_LIGHT_GLOWING); matricesNew.popPose(); }); xOffset += width + margin / 2F; @@ -223,9 +224,9 @@ public static void drawSign(PoseStack matrices, MultiBufferSource vertexConsumer final float bottomOffset = (i + 1) * height + extraMargin; final float left = flipCustomText ? x - maxWidthLeft * size : x + margin; final float right = flipCustomText ? x + size - margin : x + (maxWidthRight + 1) * size; - RenderTrains.scheduleRender(ClientData.DATA_CACHE.getDirectionArrow(selectedIdsSorted.get(i), false, false, flipCustomText ? HorizontalAlignment.RIGHT : HorizontalAlignment.LEFT, false, margin / size, (right - left) / (bottomOffset - topOffset), backgroundColor, ARGB_WHITE, backgroundColor).resourceLocation, true, RenderTrains.QueuedRenderLayer.LIGHT_TRANSLUCENT, (matricesNew, vertexConsumer) -> { + RenderTrains.scheduleRender(IRailwaySign.getPlatformSignResource(signId, selectedIdsSorted.get(i), flipCustomText ? HorizontalAlignment.RIGHT : HorizontalAlignment.LEFT, margin / size, (right - left) / (bottomOffset - topOffset), backgroundColor, ARGB_WHITE, backgroundColor, true), true, RenderTrains.QueuedRenderLayer.LIGHT_TRANSLUCENT, (matricesNew, vertexConsumer) -> { storedMatrixTransformations.transform(matricesNew); - IDrawing.drawTexture(matricesNew, vertexConsumer, left, topOffset, 0, right, bottomOffset, 0, 0, 0, 1, 1, facing, -1, MAX_LIGHT_GLOWING); + IDrawing.drawTexture(matricesNew, vertexConsumer, left, topOffset + y, 0, right, bottomOffset, 0, 0, 0, 1, 1, facing, -1, MAX_LIGHT_GLOWING); matricesNew.popPose(); }); } diff --git a/common/src/main/java/ziyue/tjmetro/screen/RailwaySignDoubleScreen.java b/common/src/main/java/ziyue/tjmetro/screen/RailwaySignDoubleScreen.java index dd175ba..e57584a 100644 --- a/common/src/main/java/ziyue/tjmetro/screen/RailwaySignDoubleScreen.java +++ b/common/src/main/java/ziyue/tjmetro/screen/RailwaySignDoubleScreen.java @@ -27,6 +27,7 @@ import ziyue.tjmetro.block.BlockRailwaySignTianjinDouble; import ziyue.tjmetro.block.BlockRailwaySignWallDouble; import ziyue.tjmetro.block.base.BlockRailwaySignBase; +import ziyue.tjmetro.block.base.IRailwaySign; import ziyue.tjmetro.packet.PacketGuiServer; import java.util.*; @@ -56,6 +57,7 @@ public class RailwaySignDoubleScreen extends ScreenMapper implements IGui protected final List exitsForList = new ArrayList<>(); protected final List platformsForList = new ArrayList<>(); protected final List routesForList = new ArrayList<>(); + protected final List stationsForList = new ArrayList<>(); protected final List allSignIds = new ArrayList<>(); protected final Button[][] buttonsEdit; @@ -96,8 +98,8 @@ public RailwaySignDoubleScreen(BlockPos signPos) { Collections.sort(platforms); platforms.stream().map(platform -> new DataConverter(platform.id, platform.name + " " + IGui.mergeStations(ClientData.DATA_CACHE.requestPlatformIdToRoutes(platform.id).stream().map(route -> route.stationDetails.get(route.stationDetails.size() - 1).stationName).collect(Collectors.toList())), 0)).forEach(platformsForList::add); - final Map routeMap = ClientData.DATA_CACHE.stationIdToRoutes.get(station.id); - routeMap.forEach((color, route) -> routesForList.add(new DataConverter(route.color, route.name, route.color))); + ClientData.DATA_CACHE.getAllRoutesIncludingConnectingStations(station).forEach((color, route) -> routesForList.add(new DataConverter(route.color, route.name, route.color))); + ClientData.DATA_CACHE.getConnectingStationsIncludingThisOne(station).forEach(connectingStation -> stationsForList.add(new DataConverter(connectingStation.id, connectingStation.name, connectingStation.color))); } } catch (Exception e) { if (ClientData.DATA_CACHE.stationIdToRoutes.get(RailwayData.getStation(ClientData.STATIONS, ClientData.DATA_CACHE, signPos).id) == null) @@ -341,11 +343,12 @@ protected void edit(int line, int editingIndex) { protected void setNewSignId(String newSignId) { if (editingIndex >= 0 && editingIndex < signIds[0].length) { signIds[line][editingIndex] = newSignId; - final boolean isExitLetter = newSignId != null && (newSignId.equals(BlockRailwaySign.SignType.EXIT_LETTER.toString()) || newSignId.equals(BlockRailwaySign.SignType.EXIT_LETTER_FLIPPED.toString())); - final boolean isPlatform = newSignId != null && (newSignId.equals(BlockRailwaySign.SignType.PLATFORM.toString()) || newSignId.equals(BlockRailwaySign.SignType.PLATFORM_FLIPPED.toString())); - final boolean isLine = newSignId != null && (newSignId.equals(BlockRailwaySign.SignType.LINE.toString()) || newSignId.equals(BlockRailwaySign.SignType.LINE_FLIPPED.toString())); - if ((isExitLetter || isPlatform || isLine) && minecraft != null) { - UtilitiesClient.setScreen(minecraft, new DashboardListSelectorScreen(this, isExitLetter ? exitsForList : isPlatform ? platformsForList : routesForList, selectedIds.get(line), false, false)); + final boolean isExitLetter = IRailwaySign.signIsExit(newSignId); + final boolean isLine = IRailwaySign.signIsLine(newSignId); + final boolean isPlatform = IRailwaySign.signIsPlatform(newSignId); + final boolean isStation = IRailwaySign.signIsStation(newSignId); + if ((isExitLetter || isPlatform || isLine || isStation) && minecraft != null) { + UtilitiesClient.setScreen(minecraft, new DashboardListSelectorScreen(this, isExitLetter ? exitsForList : isPlatform ? platformsForList : isLine ? routesForList : stationsForList, selectedIds.get(line), false, false)); } } } diff --git a/common/src/main/java/ziyue/tjmetro/screen/RailwaySignScreen.java b/common/src/main/java/ziyue/tjmetro/screen/RailwaySignScreen.java index c827e79..b8e5f66 100644 --- a/common/src/main/java/ziyue/tjmetro/screen/RailwaySignScreen.java +++ b/common/src/main/java/ziyue/tjmetro/screen/RailwaySignScreen.java @@ -26,6 +26,7 @@ import ziyue.tjmetro.TianjinMetro; import ziyue.tjmetro.block.BlockStationNameEntranceTianjin; import ziyue.tjmetro.block.base.BlockRailwaySignBase; +import ziyue.tjmetro.block.base.IRailwaySign; import ziyue.tjmetro.packet.PacketGuiServer; import java.util.*; @@ -322,10 +323,10 @@ protected void edit(int editingIndex) { protected void setNewSignId(String newSignId) { if (editingIndex >= 0 && editingIndex < signIds.length) { signIds[editingIndex] = newSignId; - final boolean isExitLetter = newSignId != null && (newSignId.equals(BlockRailwaySign.SignType.EXIT_LETTER.toString()) || newSignId.equals(BlockRailwaySign.SignType.EXIT_LETTER_FLIPPED.toString())); - final boolean isPlatform = newSignId != null && (newSignId.equals(BlockRailwaySign.SignType.PLATFORM.toString()) || newSignId.equals(BlockRailwaySign.SignType.PLATFORM_FLIPPED.toString())); - final boolean isLine = newSignId != null && (newSignId.equals(BlockRailwaySign.SignType.LINE.toString()) || newSignId.equals(BlockRailwaySign.SignType.LINE_FLIPPED.toString())); - final boolean isStation = newSignId != null && (newSignId.equals(BlockRailwaySign.SignType.STATION.toString()) || newSignId.equals(BlockRailwaySign.SignType.STATION_FLIPPED.toString())); + final boolean isExitLetter = IRailwaySign.signIsExit(newSignId); + final boolean isLine = IRailwaySign.signIsLine(newSignId); + final boolean isPlatform = IRailwaySign.signIsPlatform(newSignId); + final boolean isStation = IRailwaySign.signIsStation(newSignId); if ((isExitLetter || isPlatform || isLine || isStation) && minecraft != null) { UtilitiesClient.setScreen(minecraft, new DashboardListSelectorScreen(this, isExitLetter ? exitsForList : isPlatform ? platformsForList : isLine ? routesForList : stationsForList, selectedIds, false, false)); } diff --git a/common/src/main/resources/assets/tjmetro/lang/en_us.json b/common/src/main/resources/assets/tjmetro/lang/en_us.json index 5b14c69..f143672 100644 --- a/common/src/main/resources/assets/tjmetro/lang/en_us.json +++ b/common/src/main/resources/assets/tjmetro/lang/en_us.json @@ -53,7 +53,7 @@ "gui.tjmetro.default_color": "Default Color", "gui.tjmetro.custom_content": "Display content", "gui.tjmetro.terminus_cjk": "终点站", - "gui.tjmetro.terminus": "Terminus Station", + "gui.tjmetro.terminus": "Terminal Station", "gui.tjmetro.next_station_cjk": "下一站", "gui.tjmetro.next_station": "Next Station", "gui.tjmetro.station_pinyin": "%s ZHAN", @@ -62,6 +62,8 @@ "sign.tjmetro.tianjin_metro": "Tianjin Metro", "sign.tjmetro.train_cjk": "列车", "sign.tjmetro.train": "Train", + "sign.tjmetro.station_cjk": "站名", + "sign.tjmetro.station": "Station", "sign.tjmetro.emergency_exit_cjk": "紧急出口", "sign.tjmetro.emergency_exit": "Emergency Exit", "sign.tjmetro.no_throughfare_cjk": "禁止通行", @@ -76,10 +78,24 @@ "sign.tjmetro.railway_station": "Railway Station", "sign.tjmetro.accessible_toilet_cjk": "无障碍卫生间", "sign.tjmetro.accessible_toilet": "Accessible Toilet", + "sign.tjmetro.fare_adjustment_cjk": "补票", + "sign.tjmetro.fare_adjustment": "Fare Adjustment", + "sign.tjmetro.inquiry_cjk": "问询", + "sign.tjmetro.inquiry": "Inquiry", + "sign.tjmetro.bound_for_cjk": "%s方向", + "sign.tjmetro.bound_for": "To %s", + "sign.tjmetro.accessible_passage_cjk": "无障碍通道", + "sign.tjmetro.accessible_passage": "Accessible Passage", + "sign.tjmetro.toilet_cjk": "卫生间", + "sign.tjmetro.toilet": "Toilet", "sign.tjmetro.no_entry_bmt_cjk": "乘客止步", "sign.tjmetro.no_entry_bmt": "No Entry", "sign.tjmetro.to_subway_bmt_cjk": "乘车", "sign.tjmetro.to_subway_bmt": "To Subway", + "sign.tjmetro.exit_bmt_cjk": "出口", + "sign.tjmetro.exit_bmt": "Exit", + "sign.tjmetro.tickets_bmt_cjk": "售票处", + "sign.tjmetro.tickets_bmt": "Tickets", "button.tjmetro.tianjin_metro_options": "Tianjin Metro Options", diff --git a/common/src/main/resources/assets/tjmetro/textures/sign/accessible_passage.png b/common/src/main/resources/assets/tjmetro/textures/sign/accessible_passage.png new file mode 100644 index 0000000000000000000000000000000000000000..2dcf5033763928a94118008afd5d95873d544f95 GIT binary patch literal 4146 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 ZI8(1c=i9!=9%=v8`z{OQReJK%{tepKOsfC@ literal 0 HcmV?d00001 diff --git a/common/src/main/resources/assets/tjmetro/textures/sign/bound_for.png b/common/src/main/resources/assets/tjmetro/textures/sign/bound_for.png new file mode 100644 index 0000000000000000000000000000000000000000..ae57eddeeaeb64eed5b956c6d79c2d3ba62b7685 GIT binary patch literal 1108 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 M&PovPn<+K@8v)G)pa1{> literal 0 HcmV?d00001 diff --git a/common/src/main/resources/assets/tjmetro/textures/sign/exit.png b/common/src/main/resources/assets/tjmetro/textures/sign/exit.png new file mode 100644 index 0000000000000000000000000000000000000000..407f1b5ce12be715ab2b1473a7ea22786c9fc4f7 GIT binary patch literal 3316 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 KBY)nCHU9>o8`E$A literal 0 HcmV?d00001 diff --git a/common/src/main/resources/assets/tjmetro/textures/sign/exit_bmt_down.png b/common/src/main/resources/assets/tjmetro/textures/sign/exit_bmt_down.png new file mode 100644 index 0000000000000000000000000000000000000000..556ee6d8bb3655e92e7cc21d6b8a23b3bcbd88cb GIT binary patch literal 3582 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 literal 0 HcmV?d00001 diff --git a/common/src/main/resources/assets/tjmetro/textures/sign/exit_bmt_up.png b/common/src/main/resources/assets/tjmetro/textures/sign/exit_bmt_up.png new file mode 100644 index 0000000000000000000000000000000000000000..8a6a68b57f5fb208a7914bd42d24011391d08032 GIT binary patch literal 3507 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 literal 0 HcmV?d00001 diff --git a/common/src/main/resources/assets/tjmetro/textures/sign/fare_adjustment.png b/common/src/main/resources/assets/tjmetro/textures/sign/fare_adjustment.png new file mode 100644 index 0000000000000000000000000000000000000000..d9f73c25c014420da460832e422d57da29ca0c35 GIT binary patch literal 4409 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 literal 0 HcmV?d00001 diff --git a/common/src/main/resources/assets/tjmetro/textures/sign/inquiry.png b/common/src/main/resources/assets/tjmetro/textures/sign/inquiry.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d882175a605e4b877518899f7ba6b9fe35e1d1 GIT binary patch literal 3480 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 inXhiYykkDVujq)_@PnbE4gUexLmadK literal 0 HcmV?d00001 diff --git a/common/src/main/resources/assets/tjmetro/textures/sign/stairs.png b/common/src/main/resources/assets/tjmetro/textures/sign/stairs.png new file mode 100644 index 0000000000000000000000000000000000000000..edf15638fc0a437324a890ff6bd95f69c4e7015f GIT binary patch literal 3558 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 + + + + + + + + + + diff --git a/icons/escalator.svg b/icons/escalator.svg new file mode 100644 index 0000000..40763e5 --- /dev/null +++ b/icons/escalator.svg @@ -0,0 +1,18 @@ + + + + + + + diff --git a/icons/exit.svg b/icons/exit.svg new file mode 100644 index 0000000..91d533e --- /dev/null +++ b/icons/exit.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + diff --git a/icons/exit_bmt.svg b/icons/exit_bmt.svg new file mode 100644 index 0000000..2e4ed5f --- /dev/null +++ b/icons/exit_bmt.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + diff --git a/icons/fare_adjustment.svg b/icons/fare_adjustment.svg new file mode 100644 index 0000000..7b90e4e --- /dev/null +++ b/icons/fare_adjustment.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + diff --git a/icons/inquiry.svg b/icons/inquiry.svg new file mode 100644 index 0000000..01b8b38 --- /dev/null +++ b/icons/inquiry.svg @@ -0,0 +1,17 @@ + + + + + + + diff --git a/icons/stairs.svg b/icons/stairs.svg new file mode 100644 index 0000000..4cd7385 --- /dev/null +++ b/icons/stairs.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + diff --git a/icons/toilet.svg b/icons/toilet.svg new file mode 100644 index 0000000..dde1ebe --- /dev/null +++ b/icons/toilet.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + +