Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
517aabf219 | ||
|
|
40edf05109 | ||
|
|
4e5a92aff7 | ||
|
|
afc90899f9 | ||
|
|
a09e6058a9 | ||
|
|
f4546ac4f8 | ||
|
|
15e9be364a | ||
|
|
e774dd5e10 | ||
|
|
e864b9b292 | ||
|
|
0866dbee98 | ||
|
|
208f7ffc41 | ||
|
|
4f1edf1319 | ||
|
|
88a6021546 | ||
|
|
a05267a566 | ||
|
|
dd3413dcaa | ||
|
|
26b88a5f27 | ||
|
|
87903fead7 | ||
|
|
1166c651d2 | ||
|
|
7f42cc1de8 | ||
|
|
34394ef8a5 | ||
|
|
ff3904ec1a | ||
|
|
c3671609d3 | ||
|
|
7c0d3f4bd6 | ||
|
|
cd4f3617be | ||
|
|
256bc60d35 | ||
|
|
70426ab7b3 | ||
|
|
0030c02e9a | ||
|
|
7382db1bbd | ||
|
|
b669cbecf1 |
No files matched your search
@@ -0,0 +1,17 @@
|
||||
name: Optimize images
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ master, main, release/** ]
|
||||
paths:
|
||||
- "**/*.png"
|
||||
|
||||
push:
|
||||
branches: [ master, main, release/** ]
|
||||
paths:
|
||||
- "**/*.png"
|
||||
|
||||
jobs:
|
||||
optimize-images:
|
||||
uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/optimize-images.yml@master
|
||||
secrets: inherit
|
||||
@@ -1,9 +1,10 @@
|
||||
// Add your dependencies here
|
||||
|
||||
dependencies {
|
||||
api("com.github.GTNewHorizons:GTNHLib:0.6.38:dev")
|
||||
implementation("com.github.GTNewHorizons:NotEnoughItems:2.7.69-GTNH:dev")
|
||||
api("com.github.GTNewHorizons:GTNHLib:0.9.47:dev")
|
||||
implementation("com.github.GTNewHorizons:NotEnoughItems:2.8.84-GTNH:dev")
|
||||
|
||||
testImplementation(platform('org.junit:junit-bom:5.9.2'))
|
||||
testImplementation('org.junit.jupiter:junit-jupiter')
|
||||
testRuntimeOnly ('org.junit.platform:junit-platform-launcher')
|
||||
}
|
||||
@@ -39,9 +39,33 @@ remoteMappings = https\://raw.githubusercontent.com/MinecraftForge/FML/1.7.10/co
|
||||
# `./gradlew runClient --username=AnotherPlayer`, or configuring this command in your IDE.
|
||||
developmentEnvironmentUserName = Developer
|
||||
|
||||
# Enables using modern Java syntax (up to version 17) via Jabel, while still targeting JVM 8.
|
||||
# See https://github.com/bsideup/jabel for details on how this works.
|
||||
enableModernJavaSyntax = true
|
||||
# Enables modern Java syntax support. Valid values:
|
||||
# - false: No modern syntax, Java 8 only
|
||||
# - jabel: Jabel syntax-only support, compiles to J8 bytecode
|
||||
# - jvmDowngrader: Full modern Java via JVM Downgrader (syntax + stdlib APIs)
|
||||
# - modern: Native modern Java bytecode, no downgrading
|
||||
enableModernJavaSyntax = jabel
|
||||
|
||||
# If set, ignores the above setting and compiles with the given toolchain. This may cause unexpected issues,
|
||||
# and should *not* be used in most situations. -1 disables this.
|
||||
# forceToolchainVersion = -1
|
||||
|
||||
# Target JVM version for JVM Downgrader bytecode downgrading.
|
||||
# Only used when enableModernJavaSyntax = jvmDowngrader
|
||||
# downgradeTargetVersion = 8
|
||||
|
||||
# Comma-separated list of Java versions for multi-release jar support (JVM Downgrader only).
|
||||
# Classes will be available in META-INF/versions/N/ for each version N in this list.
|
||||
# Default: "21,25" (J25+ gets native classes, J21-24 gets partial downgrade, J8-20 gets full downgrade).
|
||||
# jvmDowngraderMultiReleaseVersions = 21,25
|
||||
|
||||
# Specifies how JVM Downgrader API stubs are provided. Options:
|
||||
# - shade: Shade minimized stubs into the jar
|
||||
# - gtnhlib: GTNHLib provides stubs at runtime (adds version constraint)
|
||||
# - external: Another dependency provides stubs (no constraint, no warning)
|
||||
# - (empty): Warning reminding you to configure stubs
|
||||
# Note: 'shade' option requires you to verify license compliance, see: https://github.com/unimined/JvmDowngrader/blob/main/LICENSE.md
|
||||
# jvmDowngraderStubsProvider =
|
||||
|
||||
# Enables injecting missing generics into the decompiled source code for a better coding experience.
|
||||
# Turns most publicly visible List, Map, etc. into proper List<E>, Map<K, V> types.
|
||||
@@ -96,7 +120,9 @@ usesMixinDebug = false
|
||||
# Specify the location of your implementation of IMixinConfigPlugin. Leave it empty otherwise.
|
||||
mixinPlugin =
|
||||
|
||||
# Specify the package that contains all of your Mixins. You may only place Mixins in this package or the build will fail!
|
||||
# Specify the package that contains all of your Mixins. The package must exist or
|
||||
# the build will fail. If you have a package property defined in your mixins.<modid>.json,
|
||||
# it must match with this or the build will fail.
|
||||
mixinsPackage = mixins
|
||||
|
||||
# Specify the core mod entry class if you use a core mod. This class must implement IFMLLoadingPlugin!
|
||||
@@ -172,6 +198,12 @@ curseForgeRelations =
|
||||
# projects. New projects should not use this parameter.
|
||||
# customArchiveBaseName =
|
||||
|
||||
# Optional parameter to customize the default working directory used by the runClient* tasks. Relative to the project directory.
|
||||
# runClientWorkingDirectory = run/client
|
||||
|
||||
# Optional parameter to customize the default working directory used by the runServer* tasks. Relative to the project directory.
|
||||
# runServerWorkingDirectory = run/server
|
||||
|
||||
# Optional parameter to have the build automatically fail if an illegal version is used.
|
||||
# This can be useful if you e.g. only want to allow versions in the form of '1.1.xxx'.
|
||||
# The check is ONLY performed if the version is a git tag.
|
||||
@@ -201,3 +233,6 @@ curseForgeRelations =
|
||||
# This is meant to be set in $HOME/.gradle/gradle.properties.
|
||||
# ideaCheckSpotlessOnBuild = true
|
||||
|
||||
# Non-GTNH properties
|
||||
org.gradle.configuration-cache = true
|
||||
org.gradle.parallel = true
|
||||
@@ -0,0 +1,12 @@
|
||||
#This file is generated by updateDaemonJvm
|
||||
toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/1630f7ebef05444cb27a2709ea0249b3/redirect
|
||||
toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect
|
||||
toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/1630f7ebef05444cb27a2709ea0249b3/redirect
|
||||
toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect
|
||||
toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/d4fd992c9557644e637ebe98263e0ae7/redirect
|
||||
toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/faa12903720d410b387cc69ccafb1a74/redirect
|
||||
toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/1630f7ebef05444cb27a2709ea0249b3/redirect
|
||||
toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect
|
||||
toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/a4c09dd2e2d7079373d30e524bbc2829/redirect
|
||||
toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/8e1d9ee5d0f13e442218f6884a306da1/redirect
|
||||
toolchainVersion=25
|
||||
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
# Copyright © 2015 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -57,7 +57,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/b631911858264c0b6e4d6603d677ff5218766cee/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
@@ -114,7 +114,6 @@ case "$( uname )" in #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
@@ -172,7 +171,6 @@ fi
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
@@ -212,8 +210,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
|
||||
@@ -70,11 +70,10 @@ goto fail
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
|
||||
@@ -17,7 +17,7 @@ pluginManagement {
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.41'
|
||||
id 'com.gtnewhorizons.gtnhsettingsconvention' version '2.0.24'
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,11 @@ import static java.lang.Math.abs;
|
||||
import net.minecraft.util.Vec3;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
import org.joml.Vector3i;
|
||||
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.Direction;
|
||||
import com.gtnewhorizon.structurelib.coords.CoordinateSystem;
|
||||
import com.gtnewhorizon.structurelib.coords.Position;
|
||||
import com.gtnewhorizon.structurelib.util.Vec3Impl;
|
||||
|
||||
public class IntegerAxisSwap {
|
||||
@@ -91,4 +95,32 @@ public class IntegerAxisSwap {
|
||||
out[1] = forFirstAxis.get1() * point[0] + forSecondAxis.get1() * point[1] + forThirdAxis.get1() * point[2];
|
||||
out[2] = forFirstAxis.get2() * point[0] + forSecondAxis.get2() * point[1] + forThirdAxis.get2() * point[2];
|
||||
}
|
||||
|
||||
public Vector3i translate(Vector3i point) {
|
||||
return new Vector3i(
|
||||
forFirstAxis.get0() * point.x() + forFirstAxis.get1() * point.y() + forFirstAxis.get2() * point.z(),
|
||||
forSecondAxis.get0() * point.x() + forSecondAxis.get1() * point.y() + forSecondAxis.get2() * point.z(),
|
||||
forThirdAxis.get0() * point.x() + forThirdAxis.get1() * point.y() + forThirdAxis.get2() * point.z());
|
||||
}
|
||||
|
||||
public Vector3i inverseTranslate(Vector3i point) {
|
||||
return new Vector3i(
|
||||
forFirstAxis.get0() * point.x() + forSecondAxis.get0() * point.y() + forThirdAxis.get0() * point.z(),
|
||||
forFirstAxis.get1() * point.x() + forSecondAxis.get1() * point.y() + forThirdAxis.get1() * point.z(),
|
||||
forFirstAxis.get2() * point.x() + forSecondAxis.get2() * point.y() + forThirdAxis.get2() * point.z());
|
||||
}
|
||||
|
||||
public <C extends CoordinateSystem<C, ?>> Position<C> translate(Position<?> point) {
|
||||
return new Position<>(
|
||||
forFirstAxis.get0() * point.x() + forFirstAxis.get1() * point.y() + forFirstAxis.get2() * point.z(),
|
||||
forSecondAxis.get0() * point.x() + forSecondAxis.get1() * point.y() + forSecondAxis.get2() * point.z(),
|
||||
forThirdAxis.get0() * point.x() + forThirdAxis.get1() * point.y() + forThirdAxis.get2() * point.z());
|
||||
}
|
||||
|
||||
public <C extends CoordinateSystem<C, ?>> Position<C> inverseTranslate(Position<?> point) {
|
||||
return new Position<>(
|
||||
forFirstAxis.get0() * point.x() + forSecondAxis.get0() * point.y() + forThirdAxis.get0() * point.z(),
|
||||
forFirstAxis.get1() * point.x() + forSecondAxis.get1() * point.y() + forThirdAxis.get1() * point.z(),
|
||||
forFirstAxis.get2() * point.x() + forSecondAxis.get2() * point.y() + forThirdAxis.get2() * point.z());
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import net.minecraftforge.common.util.ForgeDirection;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.gtnewhorizon.structurelib.alignment.IAlignment;
|
||||
import com.gtnewhorizon.structurelib.alignment.IntegerAxisSwap;
|
||||
import com.gtnewhorizon.structurelib.coords.StructureRelativeCoords;
|
||||
import com.gtnewhorizon.structurelib.util.Vec3Impl;
|
||||
|
||||
public enum ExtendedFacing {
|
||||
@@ -166,6 +167,7 @@ public enum ExtendedFacing {
|
||||
|
||||
private final String name;
|
||||
private final IntegerAxisSwap integerAxisSwap;
|
||||
private final StructureRelativeCoords coordinateSystem;
|
||||
|
||||
ExtendedFacing(String name) {
|
||||
this.name = name;
|
||||
@@ -247,6 +249,7 @@ public enum ExtendedFacing {
|
||||
this.b = b;
|
||||
this.c = c;
|
||||
integerAxisSwap = new IntegerAxisSwap(a, b, c);
|
||||
coordinateSystem = new StructureRelativeCoords(integerAxisSwap);
|
||||
}
|
||||
|
||||
public static ExtendedFacing of(ForgeDirection direction, Rotation rotation, Flip flip) {
|
||||
@@ -437,4 +440,8 @@ public enum ExtendedFacing {
|
||||
public ForgeDirection getRelativeForwardInWorld() {
|
||||
return c.getOpposite();
|
||||
}
|
||||
|
||||
public StructureRelativeCoords asCoordinateSystem() {
|
||||
return coordinateSystem;
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,10 @@ public class BlockHint extends Block {
|
||||
|
||||
@Override
|
||||
public IIcon getIcon(int aSide, int aMeta) {
|
||||
// For numbered hints, top and bottom are blank
|
||||
if (aSide <= 1 && aMeta <= 11) {
|
||||
return hint[13];
|
||||
}
|
||||
return hint[aMeta];
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.gtnewhorizon.structurelib.coords;
|
||||
|
||||
/**
|
||||
* Controller-relative coordinates. The controller is at 0,0,0. No axis swapping takes place, each axis points in the
|
||||
* same directions as the matching world axis.
|
||||
*/
|
||||
public class ControllerRelativeCoords implements CoordinateSystem<ControllerRelativeCoords, WorldCoords> {
|
||||
|
||||
public final int controllerX, controllerY, controllerZ;
|
||||
|
||||
public ControllerRelativeCoords(int controllerX, int controllerY, int controllerZ) {
|
||||
this.controllerX = controllerX;
|
||||
this.controllerY = controllerY;
|
||||
this.controllerZ = controllerZ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position<ControllerRelativeCoords> translate(Position<WorldCoords> position) {
|
||||
return translate(position, controllerX, controllerY, controllerZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position<WorldCoords> translateInverse(Position<ControllerRelativeCoords> position) {
|
||||
return translateInverse(position, controllerX, controllerY, controllerZ);
|
||||
}
|
||||
|
||||
public static Position<ControllerRelativeCoords> translate(Position<WorldCoords> position, int controllerX,
|
||||
int controllerY, int controllerZ) {
|
||||
position.sub(controllerX, controllerY, controllerZ);
|
||||
|
||||
return CoordinateSystem.transmute(position);
|
||||
}
|
||||
|
||||
public static Position<WorldCoords> translateInverse(Position<ControllerRelativeCoords> position, int controllerX,
|
||||
int controllerY, int controllerZ) {
|
||||
position.add(controllerX, controllerY, controllerZ);
|
||||
|
||||
return CoordinateSystem.transmute(position);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.gtnewhorizon.structurelib.coords;
|
||||
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
||||
|
||||
/**
|
||||
* A coordinate system is a statically-checked helper for determining which coordinates a {@link Position} has.
|
||||
* Translation methods never copy a {@link Position} - if a position must remain unchanged, it must be copied
|
||||
* separately. Most coordinate systems will have static methods that allow you to avoid allocations, though some (such
|
||||
* as the ones provided by {@link ExtendedFacing#asCoordinateSystem()}) have no reason to be static.
|
||||
*
|
||||
* @param <Self>
|
||||
* @param <Parent>
|
||||
*/
|
||||
public interface CoordinateSystem<Self extends CoordinateSystem<Self, Parent>, Parent extends CoordinateSystem<Parent, ?>> {
|
||||
|
||||
/**
|
||||
* Translates a position from the parent coordinate system into this one.
|
||||
*/
|
||||
Position<Self> translate(Position<Parent> position);
|
||||
|
||||
/**
|
||||
* Translates a position from this coordinate system into the parent.
|
||||
*/
|
||||
Position<Parent> translateInverse(Position<Self> position);
|
||||
|
||||
/**
|
||||
* Helper method that converts a position's coordinate system. Should not be used unless you know it's correct.
|
||||
*/
|
||||
static <P1 extends CoordinateSystem<P1, ?>, P2 extends CoordinateSystem<P2, ?>> Position<P1> transmute(
|
||||
Position<P2> pos) {
|
||||
// noinspection unchecked
|
||||
return (Position<P1>) pos;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.gtnewhorizon.structurelib.coords;
|
||||
|
||||
import net.minecraft.util.MathHelper;
|
||||
import net.minecraft.util.Vec3;
|
||||
import net.minecraft.world.ChunkPosition;
|
||||
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector3i;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.blockpos.BlockPos;
|
||||
import com.gtnewhorizon.structurelib.util.Vec3Impl;
|
||||
|
||||
/**
|
||||
* A {@link BlockPos} with a statically-checked coordinate system.
|
||||
*/
|
||||
public class Position<C extends CoordinateSystem<C, ?>> extends BlockPos {
|
||||
|
||||
public Position() {
|
||||
|
||||
}
|
||||
|
||||
public Position(int x, int y, int z) {
|
||||
super(x, y, z);
|
||||
}
|
||||
|
||||
public Position(float x, float y, float z) {
|
||||
super(MathHelper.floor_float(x), MathHelper.floor_float(y), MathHelper.floor_float(z));
|
||||
}
|
||||
|
||||
public Position(double x, double y, double z) {
|
||||
super(MathHelper.floor_double(x), MathHelper.floor_double(y), MathHelper.floor_double(z));
|
||||
}
|
||||
|
||||
public Position(ChunkPosition chunkPosition) {
|
||||
super(chunkPosition);
|
||||
}
|
||||
|
||||
public Position(Vector3i v) {
|
||||
super(v.x, v.y, v.z);
|
||||
}
|
||||
|
||||
public Position(Vector3f v) {
|
||||
this(v.x, v.y, v.z);
|
||||
}
|
||||
|
||||
public Position(Vec3 v) {
|
||||
this(v.xCoord, v.yCoord, v.zCoord);
|
||||
}
|
||||
|
||||
public Position(Vec3Impl v) {
|
||||
this(v.get0(), v.get1(), v.get2());
|
||||
}
|
||||
|
||||
public Position<C> copy() {
|
||||
return new Position<>(x, y, z);
|
||||
}
|
||||
|
||||
public Vector3i toVector3i() {
|
||||
return new Vector3i(x, y, z);
|
||||
}
|
||||
|
||||
public Vector3f toVector3f() {
|
||||
return new Vector3f(x, y, z);
|
||||
}
|
||||
|
||||
public Vec3 toVec3() {
|
||||
return Vec3.createVectorHelper(x, y, z);
|
||||
}
|
||||
|
||||
public Vec3Impl toVec3Impl() {
|
||||
return new Vec3Impl(x, y, z);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.gtnewhorizon.structurelib.coords;
|
||||
|
||||
/// The structure definition-local coords. 0,0,0 is the front-most left-most top-most element. Coordinates increase as
|
||||
/// you go down, right, or back.
|
||||
public class StructureDefinitionCoords implements CoordinateSystem<StructureDefinitionCoords, StructureRelativeCoords> {
|
||||
|
||||
public final int offsetX, offsetY, offsetZ;
|
||||
|
||||
public StructureDefinitionCoords(int offsetX, int offsetY, int offsetZ) {
|
||||
this.offsetX = offsetX;
|
||||
this.offsetY = offsetY;
|
||||
this.offsetZ = offsetZ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position<StructureDefinitionCoords> translate(Position<StructureRelativeCoords> position) {
|
||||
return translate(position, offsetX, offsetY, offsetZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position<StructureRelativeCoords> translateInverse(Position<StructureDefinitionCoords> position) {
|
||||
return translateInverse(position, offsetX, offsetY, offsetZ);
|
||||
}
|
||||
|
||||
public static Position<StructureDefinitionCoords> translate(Position<StructureRelativeCoords> position, int offsetX,
|
||||
int offsetY, int offsetZ) {
|
||||
position.add(offsetX, offsetY, offsetZ);
|
||||
|
||||
return CoordinateSystem.transmute(position);
|
||||
}
|
||||
|
||||
public static Position<StructureRelativeCoords> translateInverse(Position<StructureDefinitionCoords> position,
|
||||
int offsetX, int offsetY, int offsetZ) {
|
||||
position.sub(offsetX, offsetY, offsetZ);
|
||||
|
||||
return CoordinateSystem.transmute(position);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.gtnewhorizon.structurelib.coords;
|
||||
|
||||
import com.gtnewhorizon.structurelib.alignment.IntegerAxisSwap;
|
||||
|
||||
/**
|
||||
* Coordinates relative to the structure. 0,0,0 is the controller. 0,0,1 is the block immediately behind the controller
|
||||
* (looking at it from its face). 1,0,0 is the block to the right of the controller. 0,1,0 is the block below the
|
||||
* controller.
|
||||
*/
|
||||
public class StructureRelativeCoords implements CoordinateSystem<StructureRelativeCoords, ControllerRelativeCoords> {
|
||||
|
||||
public final IntegerAxisSwap axisSwap;
|
||||
|
||||
public StructureRelativeCoords(IntegerAxisSwap axisSwap) {
|
||||
this.axisSwap = axisSwap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position<StructureRelativeCoords> translate(Position<ControllerRelativeCoords> position) {
|
||||
position.set(axisSwap.translate(position));
|
||||
|
||||
return CoordinateSystem.transmute(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position<ControllerRelativeCoords> translateInverse(Position<StructureRelativeCoords> position) {
|
||||
position.set(axisSwap.inverseTranslate(position));
|
||||
|
||||
return CoordinateSystem.transmute(position);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.gtnewhorizon.structurelib.coords;
|
||||
|
||||
/**
|
||||
* World block coordinates
|
||||
*/
|
||||
public class WorldCoords implements CoordinateSystem<WorldCoords, WorldCoords> {
|
||||
|
||||
public static final WorldCoords INSTANCE = new WorldCoords();
|
||||
|
||||
private WorldCoords() {}
|
||||
|
||||
@Override
|
||||
public Position<WorldCoords> translate(Position<WorldCoords> position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position<WorldCoords> translateInverse(Position<WorldCoords> position) {
|
||||
return position;
|
||||
}
|
||||
}
|
||||
@@ -164,40 +164,17 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
|
||||
list.onGuiInit(this);
|
||||
autoCompleteList.onGuiInit(this);
|
||||
|
||||
addButton(
|
||||
new GuiButton(
|
||||
ADD_BTN,
|
||||
guiLeft + 12,
|
||||
guiTop + 157,
|
||||
47,
|
||||
20,
|
||||
I18n.format("item.structurelib.constructableTrigger.gui.add")));
|
||||
addButton(
|
||||
new GuiButton(
|
||||
UNSET_BTN,
|
||||
guiLeft + 65,
|
||||
guiTop + 157,
|
||||
47,
|
||||
20,
|
||||
I18n.format("item.structurelib.constructableTrigger.gui.unset")));
|
||||
addButton(
|
||||
new GuiButton(
|
||||
WIPE_BTN,
|
||||
guiLeft + 118,
|
||||
guiTop + 157,
|
||||
47,
|
||||
20,
|
||||
I18n.format("item.structurelib.constructableTrigger.gui.wipe")));
|
||||
addButton(new GuiButton(ADD_BTN, guiLeft + 12, guiTop + 157, 47, 20, I18n.format(I18N_PREFIX + "add")));
|
||||
addButton(new GuiButton(UNSET_BTN, guiLeft + 65, guiTop + 157, 47, 20, I18n.format(I18N_PREFIX + "unset")));
|
||||
addButton(new GuiButton(WIPE_BTN, guiLeft + 118, guiTop + 157, 47, 20, I18n.format(I18N_PREFIX + "wipe")));
|
||||
|
||||
updateButtons();
|
||||
tooltipSplitCache.clear();
|
||||
info = StreamSupport
|
||||
.stream(
|
||||
Splitter.on("\\n").split(
|
||||
StatCollector.translateToLocal("item.structurelib.constructableTrigger.gui.info"))
|
||||
.spliterator(),
|
||||
Splitter.on("\\n").split(StatCollector.translateToLocal(I18N_PREFIX + "info")).spliterator(),
|
||||
false)
|
||||
.flatMap(line -> fontRendererObj.listFormattedStringToWidth(line, width * 3 / 5).stream())
|
||||
.flatMap(line -> fontRendererObj.listFormattedStringToWidth(line, width * 1 / 3).stream())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@@ -241,8 +218,7 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
|
||||
actionPerformed(but);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<GuiButton> getButtonList() {
|
||||
public List<GuiButton> getButtonList() {
|
||||
return buttonList;
|
||||
}
|
||||
|
||||
@@ -285,6 +261,11 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
|
||||
switch (aKey) {
|
||||
case Keyboard.KEY_TAB:
|
||||
if (key.isFocused()) {
|
||||
if (autoCompleteList.getSelectedElement() != null) {
|
||||
key.setText(autoCompleteList.getSelectedElement());
|
||||
return;
|
||||
}
|
||||
|
||||
key.setFocused(false);
|
||||
value.setFocused(true);
|
||||
} else {
|
||||
@@ -322,11 +303,10 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (key.textboxKeyTyped(aChar, aKey)) {
|
||||
if (key.textboxKeyTyped(aChar, aKey) || value.textboxKeyTyped(aChar, aKey)) {
|
||||
updateButtons();
|
||||
return;
|
||||
}
|
||||
if (value.textboxKeyTyped(aChar, aKey)) return;
|
||||
super.keyTyped(aChar, aKey);
|
||||
}
|
||||
|
||||
@@ -337,7 +317,8 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
|
||||
boolean existing = !StringUtils.isEmpty(keyText) && ChannelDataAccessor.hasSubChannel(trigger, keyText);
|
||||
getButtonList().get(ADD_BTN).displayString = existing ? I18n.format(I18N_PREFIX + "set")
|
||||
: I18n.format(I18N_PREFIX + "add");
|
||||
getButtonList().get(ADD_BTN).enabled = !StringUtils.isBlank(value.getText())
|
||||
getButtonList().get(ADD_BTN).enabled = !StringUtils.isBlank(key.getText())
|
||||
&& !StringUtils.isBlank(value.getText())
|
||||
&& Integer.parseInt(value.getText()) > 0;
|
||||
getButtonList().get(UNSET_BTN).enabled = existing && !StringUtils.isBlank(value.getText());
|
||||
|
||||
@@ -397,22 +378,10 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
|
||||
int topLeftY = (this.height - this.getYSize()) / 2;
|
||||
drawTexturedModalRect(topLeftX, topLeftY, 0, 0, getXSize(), getYSize());
|
||||
list.drawScreen(mX, mY, partialTick);
|
||||
fontRendererObj.drawString(
|
||||
I18n.format("item.structurelib.constructableTrigger.gui.title"),
|
||||
guiLeft + 12,
|
||||
guiTop + 9,
|
||||
0);
|
||||
fontRendererObj.drawString(
|
||||
I18n.format("item.structurelib.constructableTrigger.gui.key"),
|
||||
guiLeft + 12,
|
||||
guiTop + 122,
|
||||
0);
|
||||
fontRendererObj.drawString(I18n.format(I18N_PREFIX + "title"), guiLeft + 12, guiTop + 9, 0);
|
||||
fontRendererObj.drawString(I18n.format(I18N_PREFIX + "key"), guiLeft + 12, guiTop + 122, 0);
|
||||
key.drawTextBox();
|
||||
fontRendererObj.drawString(
|
||||
I18n.format("item.structurelib.constructableTrigger.gui.value"),
|
||||
guiLeft + 12,
|
||||
guiTop + 142,
|
||||
0);
|
||||
fontRendererObj.drawString(I18n.format(I18N_PREFIX + "value"), guiLeft + 12, guiTop + 142, 0);
|
||||
value.drawTextBox();
|
||||
}
|
||||
|
||||
@@ -455,7 +424,7 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
|
||||
tooltip.addAll(
|
||||
fontRendererObj.listFormattedStringToWidth(
|
||||
StatCollector.translateToLocalFormatted(
|
||||
"item.structurelib.constructableTrigger.gui.channels.from",
|
||||
I18N_PREFIX + "channels.from",
|
||||
Loader.instance().getIndexedModList().get(e.getKey()).getName()),
|
||||
maxLine));
|
||||
tooltip.addAll(
|
||||
|
||||
@@ -28,11 +28,12 @@ public class ItemConstructableTrigger extends Item {
|
||||
|
||||
@Override
|
||||
public ItemStack onItemRightClick(ItemStack stack, World world, EntityPlayer player) {
|
||||
if (!world.isRemote && getMovingObjectPositionFromPlayer(world, player, true) == null) {
|
||||
if (player.isSneaking()) {
|
||||
StructureLib.instance().proxy().displayConfigGUI("registries");
|
||||
} else {
|
||||
if (getMovingObjectPositionFromPlayer(world, player, true) == null) {
|
||||
// channel gui is opened from server and config gui from client
|
||||
if (!world.isRemote && !player.isSneaking()) {
|
||||
player.openGui(StructureLib.instance(), 0, world, player.inventory.currentItem, 0, 0);
|
||||
} else if (world.isRemote && player.isSneaking()) {
|
||||
StructureLib.instance().proxy().displayConfigGUI("registries");
|
||||
}
|
||||
}
|
||||
return super.onItemRightClick(stack, world, player);
|
||||
|
||||
@@ -3,8 +3,11 @@ package com.gtnewhorizon.structurelib.structure;
|
||||
import static com.gtnewhorizon.structurelib.structure.IStructureWalker.ignoreBlockUnloaded;
|
||||
import static com.gtnewhorizon.structurelib.structure.IStructureWalker.skipBlockUnloaded;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.ItemStack;
|
||||
@@ -15,6 +18,8 @@ import com.gtnewhorizon.structurelib.StructureLibAPI;
|
||||
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
|
||||
import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
||||
import com.gtnewhorizon.structurelib.coords.Position;
|
||||
import com.gtnewhorizon.structurelib.coords.StructureDefinitionCoords;
|
||||
|
||||
/**
|
||||
* This is the structure definition of your multi. You will have one of these for each multi.
|
||||
@@ -319,6 +324,65 @@ public interface IStructureDefinition<T> {
|
||||
return walker.getBuilt();
|
||||
}
|
||||
|
||||
default void iterate(String piece, World world, ExtendedFacing extendedFacing, int basePositionX, int basePositionY,
|
||||
int basePositionZ, int basePositionA, int basePositionB, int basePositionC, IStructureWalker<T> predicate) {
|
||||
StructureUtility.iterateV2(
|
||||
getStructureFor(piece),
|
||||
world,
|
||||
extendedFacing,
|
||||
basePositionX,
|
||||
basePositionY,
|
||||
basePositionZ,
|
||||
basePositionA,
|
||||
basePositionB,
|
||||
basePositionC,
|
||||
predicate,
|
||||
"customIterate");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the controller position for a piece. Controller positions are denoted by a tilde.
|
||||
*
|
||||
* @param piece The piece name
|
||||
* @return The controller position, or null if it doesn't exist for the given piece
|
||||
*/
|
||||
Position<StructureDefinitionCoords> getControllerPosition(String piece);
|
||||
|
||||
/**
|
||||
* Gets the coordinate system for a piece. This is used to translate structure definition coordinates into structure
|
||||
* relative coordinates.
|
||||
*
|
||||
* @param piece The piece
|
||||
* @return The coordinate system
|
||||
* @throws IllegalArgumentException When the piece does not exist
|
||||
*/
|
||||
StructureDefinitionCoords getCoordinateSystem(String piece);
|
||||
|
||||
/**
|
||||
* Gets a socket from a piece.
|
||||
*
|
||||
* @param piece The piece name
|
||||
* @param socket The socket character
|
||||
* @return The socket's position
|
||||
* @throws IllegalArgumentException When the piece does not exist
|
||||
* @throws IllegalArgumentException When the socket does not exist
|
||||
* @throws IllegalStateException When more than one socket exists with this character
|
||||
* @see StructureDefinition.Builder#addSocket(char, char)
|
||||
*/
|
||||
Position<StructureDefinitionCoords> getSocket(String piece, char socket);
|
||||
|
||||
/**
|
||||
* Gets all sockets with a given character for a piece.
|
||||
*
|
||||
* @param piece The piece name
|
||||
* @param socket The socket character
|
||||
* @return The list of socket positions, or an empty list if the socket is not present
|
||||
* @throws IllegalArgumentException When the piece does not exist
|
||||
* @see StructureDefinition.Builder#addSocket(char, char)
|
||||
*/
|
||||
@Nonnull
|
||||
List<Position<StructureDefinitionCoords>> getAllSockets(String piece, char socket);
|
||||
|
||||
/**
|
||||
* Low level utility.
|
||||
*
|
||||
|
||||
@@ -2,8 +2,25 @@ package com.gtnewhorizon.structurelib.structure;
|
||||
|
||||
import net.minecraft.world.World;
|
||||
|
||||
interface IStructureWalker<T> {
|
||||
/**
|
||||
* A functional interface that is used to iterate over an arbitrary structure.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface IStructureWalker<T> {
|
||||
|
||||
/**
|
||||
* Called once per structure element.
|
||||
*
|
||||
* @param element The current element
|
||||
* @param world The current world
|
||||
* @param x The absolute X position of the element in world coordinates
|
||||
* @param y The absolute Y position of the element in world coordinates
|
||||
* @param z The absolute Z position of the element in world coordinates
|
||||
* @param a The relative A position of the element in structure-local coordinates (pre transform)
|
||||
* @param b The relative B position of the element in structure-local coordinates (pre transform)
|
||||
* @param c The relative C position of the element in structure-local coordinates (pre transform)
|
||||
* @return False to stop iterating, true to continue.
|
||||
*/
|
||||
boolean visit(IStructureElement<T> element, World world, int x, int y, int z, int a, int b, int c);
|
||||
|
||||
default boolean blockNotLoaded(IStructureElement<T> element, World world, int x, int y, int z, int a, int b,
|
||||
|
||||
@@ -4,33 +4,49 @@ import static com.gtnewhorizon.structurelib.structure.StructureUtility.isAir;
|
||||
import static com.gtnewhorizon.structurelib.structure.StructureUtility.notAir;
|
||||
import static com.gtnewhorizon.structurelib.structure.StructureUtility.step;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import com.google.common.collect.ListMultimap;
|
||||
import com.google.common.collect.MultimapBuilder;
|
||||
import com.gtnewhorizon.structurelib.coords.Position;
|
||||
import com.gtnewhorizon.structurelib.coords.StructureDefinitionCoords;
|
||||
import com.gtnewhorizon.structurelib.util.Vec3Impl;
|
||||
|
||||
import it.unimi.dsi.fastutil.chars.Char2CharOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.chars.CharOpenHashSet;
|
||||
|
||||
public class StructureDefinition<T> implements IStructureDefinition<T> {
|
||||
|
||||
private final Map<Character, IStructureElement<T>> elements;
|
||||
private final Map<String, String> shapes;
|
||||
private final Map<String, IStructureElement<T>[]> structures;
|
||||
private final Map<String, Set<Vec3Impl>> occupiedSpaces;
|
||||
private final Map<String, Position<StructureDefinitionCoords>> controllers;
|
||||
private final Map<String, ListMultimap<Character, Position<StructureDefinitionCoords>>> socketLocations;
|
||||
|
||||
public static <B> Builder<B> builder() {
|
||||
return new Builder<>();
|
||||
}
|
||||
|
||||
private StructureDefinition(Map<Character, IStructureElement<T>> elements, Map<String, String> shapes,
|
||||
Map<String, IStructureElement<T>[]> structures, Map<String, Set<Vec3Impl>> occupiedSpaces) {
|
||||
Map<String, IStructureElement<T>[]> structures, Map<String, Set<Vec3Impl>> occupiedSpaces,
|
||||
Map<String, Position<StructureDefinitionCoords>> controllers,
|
||||
Map<String, ListMultimap<Character, Position<StructureDefinitionCoords>>> socketLocations) {
|
||||
this.elements = elements;
|
||||
this.shapes = shapes;
|
||||
this.structures = structures;
|
||||
this.occupiedSpaces = occupiedSpaces;
|
||||
this.controllers = controllers;
|
||||
this.socketLocations = socketLocations;
|
||||
}
|
||||
|
||||
public static class Builder<T> {
|
||||
@@ -41,12 +57,18 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
|
||||
private char d = '\uD000';
|
||||
private final Map<Vec3Impl, Character> navigates;
|
||||
private final Map<Character, IStructureElement<T>> elements;
|
||||
private final Map<String, String[][]> uncompiledShapes;
|
||||
private final Map<String, String> shapes;
|
||||
private final Map<String, Set<Vec3Impl>> occupiedSpaces;
|
||||
|
||||
private final Map<String, Position<StructureDefinitionCoords>> controllers = new HashMap<>();
|
||||
private final Char2CharOpenHashMap socketTranslations = new Char2CharOpenHashMap();
|
||||
private final Map<String, ListMultimap<Character, Position<StructureDefinitionCoords>>> socketLocations = new HashMap<>();
|
||||
|
||||
private Builder() {
|
||||
navigates = new HashMap<>();
|
||||
elements = new HashMap<>();
|
||||
uncompiledShapes = new HashMap<>();
|
||||
shapes = new HashMap<>();
|
||||
occupiedSpaces = new HashMap<>();
|
||||
}
|
||||
@@ -82,7 +104,69 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
|
||||
* @return this builder
|
||||
*/
|
||||
public Builder<T> addShape(String name, String[][] structurePiece) {
|
||||
uncompiledShapes.put(name, structurePiece);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use the unboxed version
|
||||
*/
|
||||
@Deprecated
|
||||
public Builder<T> addElement(Character name, IStructureElement<T> structurePiece) {
|
||||
elements.putIfAbsent(name, structurePiece);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> addElement(char name, IStructureElement<T> structurePiece) {
|
||||
elements.putIfAbsent(name, structurePiece);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Denotes {@code name} as a socket. Sockets are virtual structure elements that record their position in the
|
||||
* structure for future retrieval. They are transformed into real structure elements and do not need their own
|
||||
* element (see {@code replacement}).
|
||||
*
|
||||
* @param name The socket name character
|
||||
* @param replacement The replacement character
|
||||
* @see IStructureDefinition#getSocket(String, char)
|
||||
* @see IStructureDefinition#getAllSockets(String, char)
|
||||
*/
|
||||
public Builder<T> addSocket(char name, char replacement) {
|
||||
socketTranslations.put(name, replacement);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #addSocket(char, char)} but without the replacement. The structure must have a structure element
|
||||
* with the given character.
|
||||
*
|
||||
* @param name The socket name character
|
||||
*/
|
||||
public Builder<T> addSocket(char name) {
|
||||
// Adding a name -> name entry like this isn't ideal but this is the cleanest solution
|
||||
socketTranslations.put(name, name);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IStructureDefinition<T> build() {
|
||||
uncompiledShapes.forEach(this::compileShape);
|
||||
|
||||
Map<String, IStructureElement<T>[]> structures = compileStructureMap();
|
||||
|
||||
return new StructureDefinition<>(
|
||||
new HashMap<>(elements),
|
||||
new HashMap<>(shapes),
|
||||
structures,
|
||||
occupiedSpaces,
|
||||
controllers,
|
||||
socketLocations);
|
||||
}
|
||||
|
||||
private Builder<T> compileShape(String name, String[][] structurePiece) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
if (structurePiece.length > 0) {
|
||||
for (String[] strings : structurePiece) {
|
||||
if (strings.length > 0) {
|
||||
@@ -95,6 +179,11 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
|
||||
}
|
||||
builder.setLength(builder.length() - 1);
|
||||
}
|
||||
|
||||
// Always create a map for this piece, even if it doesn't have any sockets
|
||||
// noinspection UnstableApiUsage
|
||||
socketLocations.put(name, MultimapBuilder.hashKeys().arrayListValues().build());
|
||||
|
||||
Set<Vec3Impl> occupiedSpace = new HashSet<>();
|
||||
// these track the global current location
|
||||
int aa = 0, bb = 0, cc = 0;
|
||||
@@ -103,6 +192,15 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
|
||||
// I do know these are pretty bad variable naming, but I have no better idea
|
||||
for (int i = 0; i < builder.length(); i++) {
|
||||
char ch = builder.charAt(i);
|
||||
|
||||
// Translate the element char if it's a socket, and record the socket position
|
||||
if (socketTranslations.containsKey(ch)) {
|
||||
socketLocations.get(name).put(ch, new Position<>(aa, bb, cc));
|
||||
|
||||
ch = socketTranslations.get(ch);
|
||||
builder.setCharAt(i, ch);
|
||||
}
|
||||
|
||||
if (ch == ' ') {
|
||||
builder.setCharAt(i, A);
|
||||
ch = A;
|
||||
@@ -110,6 +208,7 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
|
||||
builder.setCharAt(i, A);
|
||||
ch = A;
|
||||
occupiedSpace.add(Vec3Impl.getFromPool(aa, bb, cc));
|
||||
controllers.put(name, new Position<>(aa, bb, cc));
|
||||
}
|
||||
if (ch == A) {
|
||||
aa++;
|
||||
@@ -148,76 +247,87 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
|
||||
|
||||
occupiedSpaces.put(name, occupiedSpace);
|
||||
|
||||
String built = builder.toString().replaceAll("[\\uA000\\uB000\\uC000]", "");
|
||||
boolean hasPlus = false;
|
||||
boolean hasMinus = false;
|
||||
|
||||
if (built.contains("+")) {
|
||||
int write = 0;
|
||||
for (int read = 0; read < builder.length(); read++) {
|
||||
char ch = builder.charAt(read);
|
||||
if (ch == A || ch == B || ch == C) {
|
||||
continue;
|
||||
}
|
||||
if (ch == '+') {
|
||||
hasPlus = true;
|
||||
} else if (ch == '-') {
|
||||
hasMinus = true;
|
||||
}
|
||||
if (read != write) builder.setCharAt(write, ch);
|
||||
write++;
|
||||
}
|
||||
builder.setLength(write);
|
||||
|
||||
if (hasPlus) {
|
||||
addElement('+', notAir());
|
||||
}
|
||||
if (built.contains("-")) {
|
||||
if (hasMinus) {
|
||||
addElement('-', isAir());
|
||||
}
|
||||
shapes.put(name, built);
|
||||
shapes.put(name, builder.toString());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use the unboxed version
|
||||
*/
|
||||
@Deprecated
|
||||
public Builder<T> addElement(Character name, IStructureElement<T> structurePiece) {
|
||||
elements.putIfAbsent(name, structurePiece);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> addElement(char name, IStructureElement<T> structurePiece) {
|
||||
elements.putIfAbsent(name, structurePiece);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IStructureDefinition<T> build() {
|
||||
Map<String, IStructureElement<T>[]> structures = compileStructureMap();
|
||||
return new StructureDefinition<>(
|
||||
new HashMap<>(elements),
|
||||
new HashMap<>(shapes),
|
||||
structures,
|
||||
occupiedSpaces);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, IStructureElement<T>[]> compileElementSetMap() {
|
||||
Set<Integer> missing = findMissing();
|
||||
if (missing.isEmpty()) {
|
||||
return shapes.entrySet().stream().collect(
|
||||
Collectors.toMap(
|
||||
Map.Entry::getKey,
|
||||
e -> e.getValue().chars().mapToObj(c -> (char) c).distinct().map(elements::get)
|
||||
.toArray(IStructureElement[]::new)));
|
||||
} else {
|
||||
throw new RuntimeException(
|
||||
"Missing Structure Element bindings for (chars as integers): "
|
||||
+ Arrays.toString(missing.toArray()));
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, IStructureElement<T>[]> compileStructureMap() {
|
||||
Set<Integer> missing = findMissing();
|
||||
if (missing.isEmpty()) {
|
||||
return shapes.entrySet().stream().collect(
|
||||
Collectors.toMap(
|
||||
Map.Entry::getKey,
|
||||
e -> e.getValue().chars().mapToObj(c -> elements.get((char) c))
|
||||
.toArray(IStructureElement[]::new)));
|
||||
} else {
|
||||
throw new RuntimeException(
|
||||
"Missing Structure Element bindings for (chars as integers): "
|
||||
+ Arrays.toString(missing.toArray()));
|
||||
CharOpenHashSet missing = findMissing();
|
||||
|
||||
if (!missing.isEmpty()) {
|
||||
throw new RuntimeException("Missing Structure Element bindings for: " + missing);
|
||||
}
|
||||
|
||||
Map<String, IStructureElement<T>[]> map = new HashMap<>();
|
||||
|
||||
for (Entry<String, String> e : shapes.entrySet()) {
|
||||
char[] chars = e.getValue().toCharArray();
|
||||
IStructureElement<T>[] compiled = new IStructureElement[chars.length];
|
||||
|
||||
int a = 0, b = 0, c = 0;
|
||||
|
||||
for (int i = 0; i < chars.length; i++) {
|
||||
char ch = chars[i];
|
||||
|
||||
IStructureElement<T> element = this.elements.get(ch);
|
||||
|
||||
compiled[i] = element;
|
||||
|
||||
if (element.isNavigating()) {
|
||||
a = (element.resetA() ? 0 : a) + element.getStepA();
|
||||
b = (element.resetB() ? 0 : b) + element.getStepB();
|
||||
c = (element.resetC() ? 0 : c) + element.getStepC();
|
||||
} else {
|
||||
a++;
|
||||
}
|
||||
}
|
||||
|
||||
if (map.put(e.getKey(), compiled) != null) {
|
||||
throw new IllegalStateException("Duplicate shape: " + e.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
private Set<Integer> findMissing() {
|
||||
return shapes.values().stream().flatMapToInt(CharSequence::chars)
|
||||
.filter(i -> !elements.containsKey((char) i)).boxed().collect(Collectors.toSet());
|
||||
private CharOpenHashSet findMissing() {
|
||||
CharOpenHashSet missing = new CharOpenHashSet();
|
||||
|
||||
for (String shape : shapes.values()) {
|
||||
for (char c : shape.toCharArray()) {
|
||||
if (!elements.containsKey(c)) {
|
||||
missing.add(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return missing;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,4 +356,61 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
|
||||
if (occupiedSpace == null) throw new NoSuchElementException(name);
|
||||
return occupiedSpace.contains(new Vec3Impl(offsetA, offsetB, offsetC));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position<StructureDefinitionCoords> getControllerPosition(String piece) {
|
||||
return controllers.get(piece);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructureDefinitionCoords getCoordinateSystem(String piece) {
|
||||
var controller = controllers.get(piece);
|
||||
|
||||
if (controller == null) {
|
||||
throw new IllegalArgumentException("Invalid piece: " + piece);
|
||||
}
|
||||
|
||||
return new StructureDefinitionCoords(controller.x, controller.y, controller.z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position<StructureDefinitionCoords> getSocket(String piece, char socket) {
|
||||
var piecePositions = socketLocations.get(piece);
|
||||
|
||||
if (piecePositions == null) {
|
||||
throw new IllegalArgumentException("Invalid piece: " + piece);
|
||||
}
|
||||
|
||||
var positions = piecePositions.get(socket);
|
||||
|
||||
if (positions.isEmpty()) {
|
||||
throw new IllegalArgumentException("Socket " + socket + " for piece " + piece + " does not exist");
|
||||
}
|
||||
|
||||
if (positions.size() > 1) {
|
||||
throw new IllegalStateException(
|
||||
"Socket " + socket
|
||||
+ " for piece "
|
||||
+ piece
|
||||
+ " has several positions, but expected one and only one");
|
||||
}
|
||||
|
||||
return positions.get(0).copy();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public List<Position<StructureDefinitionCoords>> getAllSockets(String piece, char socket) {
|
||||
var piecePositions = socketLocations.get(piece);
|
||||
|
||||
if (piecePositions == null) {
|
||||
throw new IllegalArgumentException("Invalid piece: " + piece);
|
||||
}
|
||||
|
||||
var list = new ArrayList<>(piecePositions.get(socket));
|
||||
|
||||
list.replaceAll(Position::copy);
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,10 @@ import net.minecraft.item.ItemBlock;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.ChatComponentText;
|
||||
import net.minecraft.util.ChatComponentTranslation;
|
||||
import net.minecraft.util.ChatStyle;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
import net.minecraft.util.IChatComponent;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.world.World;
|
||||
@@ -338,9 +341,7 @@ public class StructureUtility {
|
||||
ItemStack stack = new ItemStack(itemBlock, 1, itemMeta);
|
||||
if (!s.takeOne(stack, true)) {
|
||||
if (chatter != null) chatter.accept(
|
||||
new ChatComponentTranslation(
|
||||
"structurelib.autoplace.error.no_simple_block",
|
||||
stack.func_151000_E()));
|
||||
new ChatComponentTranslation("structurelib.autoplace.missing_block", stack.func_151000_E()));
|
||||
return PlaceResult.REJECT;
|
||||
}
|
||||
if (block instanceof ICustomBlockSetting) {
|
||||
@@ -447,7 +448,7 @@ public class StructureUtility {
|
||||
if (!StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, actor)) return PlaceResult.REJECT;
|
||||
if (!assumeStackPresent && !s.takeOne(stack, true)) {
|
||||
if (chatter != null) chatter.accept(
|
||||
new ChatComponentTranslation("structurelib.autoplace.error.no_item_stack", stack.func_151000_E()));
|
||||
new ChatComponentTranslation("structurelib.autoplace.missing_block", stack.func_151000_E()));
|
||||
return PlaceResult.REJECT;
|
||||
}
|
||||
if (!stack.copy().tryPlaceItemIntoWorld(actor, world, x, y, z, ForgeDirection.UP.ordinal(), 0.5f, 0.5f, 0.5f))
|
||||
@@ -1411,6 +1412,53 @@ public class StructureUtility {
|
||||
} else {
|
||||
return new IStructureElement<T>() {
|
||||
|
||||
@Override
|
||||
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||
AutoPlaceEnvironment env) {
|
||||
BlocksToPlace e = getBlocksToPlace(t, world, x, y, z, trigger, env);
|
||||
IItemSource source = env.getSource();
|
||||
EntityPlayer actor = env.getActor();
|
||||
Consumer<IChatComponent> chatter = env.getChatter();
|
||||
if (check(t, world, x, y, z)) return PlaceResult.SKIP;
|
||||
if (e.getStacks() == null) {
|
||||
ItemStack taken = source.takeOne(e.getPredicate(), true);
|
||||
return StructureUtility.survivalPlaceBlock(
|
||||
taken,
|
||||
NBTMode.EXACT,
|
||||
taken.stackTagCompound,
|
||||
false,
|
||||
world,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
source,
|
||||
actor,
|
||||
chatter);
|
||||
}
|
||||
for (ItemStack stack : e.getStacks()) {
|
||||
if (!source.takeOne(stack, true)) {
|
||||
IChatComponent name = new ChatComponentText(stack.getDisplayName());
|
||||
name.setChatStyle(new ChatStyle().setColor(EnumChatFormatting.YELLOW));
|
||||
env.getChatter()
|
||||
.accept(new ChatComponentTranslation("structurelib.autoplace.missing_block", name));
|
||||
continue;
|
||||
}
|
||||
return StructureUtility.survivalPlaceBlock(
|
||||
stack,
|
||||
NBTMode.EXACT,
|
||||
stack.stackTagCompound,
|
||||
false,
|
||||
world,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
source,
|
||||
actor,
|
||||
chatter);
|
||||
}
|
||||
return PlaceResult.REJECT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean check(T t, World world, int x, int y, int z) {
|
||||
Block worldBlock = world.getBlock(x, y, z);
|
||||
|
||||
@@ -34,11 +34,11 @@ item.structurelib.constructableTrigger.gui.title=Sub Channels
|
||||
item.structurelib.constructableTrigger.gui.key=Key:
|
||||
item.structurelib.constructableTrigger.gui.value=Value:
|
||||
item.structurelib.constructableTrigger.gui.add=Add
|
||||
item.structurelib.constructableTrigger.gui.set=Set
|
||||
item.structurelib.constructableTrigger.gui.unset=Unset
|
||||
item.structurelib.constructableTrigger.gui.wipe=§4Wipe
|
||||
item.structurelib.constructableTrigger.gui.set=Update
|
||||
item.structurelib.constructableTrigger.gui.unset=Remove
|
||||
item.structurelib.constructableTrigger.gui.wipe=§4Clear
|
||||
item.structurelib.constructableTrigger.gui.channels.from=§rMod: §9§o%s
|
||||
item.structurelib.constructableTrigger.gui.info=Many multiblocks support some degree of variation in its structure and use sub channels to indicate which variant to use.\n Sub channels are has a sequence of lower case characters as key and a positive integer as value. Most known sub channels should be available as typeahead completions.\nYou can also drag and drop items from NEI to set the corresponding sub channel value. These items all have a tooltip of §oIndicates value X for sub channel Y§r.
|
||||
item.structurelib.constructableTrigger.gui.info=Many multiblocks support some degree of variation in their structure and a sub channel allows indication of which variant to use.\n All sub channels have a sequence of lowercase characters as key and a positive integer as value. Most known sub channels should be available as typeahead completions.\n You can also drag and drop items from NEI to set the corresponding sub channel value. These items all have a tooltip of "§oIndicates§r §ovalue§r §oX§r §ofor§r §osub§r §ochannel§r §oY§r".
|
||||
|
||||
channels.structurelib.show_errors=When set, will highlight parts of the multiblock that is definitely wrong
|
||||
|
||||
@@ -51,6 +51,7 @@ structurelib.autoplace.error.not_enabled=§cThis multiblock does not support aut
|
||||
structurelib.autoplace.error.no_simple_block=§cSuggested to place §r%s§c but none was found
|
||||
structurelib.autoplace.error.no_item_stack=§cSuggested to place block by using §r%s§c but none was found
|
||||
structurelib.autoplace.warning.no_explicit_channel=§6This structure depends on nonexistent sub channel §r%s§6. Using master channel instead.
|
||||
structurelib.autoplace.missing_block=§cMissing block: %s
|
||||
|
||||
structurelib.blockhint.desc.0=Helps while building
|
||||
structurelib.blockhint.0.name=Hint 1 dot
|
||||
|
||||
|
Before Width: | Height: | Size: 273 B After Width: | Height: | Size: 126 B |
|
Before Width: | Height: | Size: 279 B After Width: | Height: | Size: 154 B |
|
Before Width: | Height: | Size: 296 B After Width: | Height: | Size: 138 B |
|
Before Width: | Height: | Size: 301 B After Width: | Height: | Size: 160 B |
|
Before Width: | Height: | Size: 282 B After Width: | Height: | Size: 137 B |
|
Before Width: | Height: | Size: 281 B After Width: | Height: | Size: 138 B |
|
Before Width: | Height: | Size: 285 B After Width: | Height: | Size: 131 B |
|
Before Width: | Height: | Size: 289 B After Width: | Height: | Size: 141 B |
|
Before Width: | Height: | Size: 293 B After Width: | Height: | Size: 132 B |
|
Before Width: | Height: | Size: 295 B After Width: | Height: | Size: 138 B |
|
Before Width: | Height: | Size: 296 B After Width: | Height: | Size: 139 B |
|
Before Width: | Height: | Size: 297 B After Width: | Height: | Size: 143 B |
|
Before Width: | Height: | Size: 255 B After Width: | Height: | Size: 99 B |
|
Before Width: | Height: | Size: 301 B After Width: | Height: | Size: 132 B |
|
Before Width: | Height: | Size: 306 B After Width: | Height: | Size: 146 B |
|
Before Width: | Height: | Size: 281 B After Width: | Height: | Size: 115 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 367 B After Width: | Height: | Size: 276 B |
|
Before Width: | Height: | Size: 667 B After Width: | Height: | Size: 606 B |
|
Before Width: | Height: | Size: 439 B After Width: | Height: | Size: 361 B |
@@ -0,0 +1,102 @@
|
||||
package com.gtnewhorizon.structurelib.coords;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
||||
import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
|
||||
import com.gtnewhorizon.structurelib.structure.StructureUtility;
|
||||
|
||||
// spotless:off
|
||||
class CoordinateTests {
|
||||
|
||||
private IStructureDefinition<?> getDefinition() {
|
||||
String[][] main = {
|
||||
{ " ", " ", " ", " ", " " },
|
||||
{ " ", " ", " ", " ", " " },
|
||||
{ " ", " ", " X ", " ", " " },
|
||||
{ " ", " ", " YY ", " ", " " },
|
||||
{ " ~ ", " ", " ", " ", " " }
|
||||
};
|
||||
|
||||
return IStructureDefinition.builder()
|
||||
.addShape("main", StructureUtility.transpose(main))
|
||||
.addSocket('X', ' ')
|
||||
.addSocket('Y', ' ')
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSocketAndCoordSystemTranslationToWorld() {
|
||||
IStructureDefinition<?> def = getDefinition();
|
||||
|
||||
// Note: the position is the same object between all of these transforms, it is never copied, its generic just
|
||||
// changes
|
||||
|
||||
var p = def.getSocket("main", 'X');
|
||||
Assertions.assertEquals(new Position<>(2, 2, 2), p);
|
||||
|
||||
var p2 = def.getCoordinateSystem("main").translateInverse(p);
|
||||
Assertions.assertEquals(new Position<>(0, -2, 2), p2);
|
||||
|
||||
var p3 = ExtendedFacing.NORTH_NORMAL_NONE.asCoordinateSystem().translateInverse(p2);
|
||||
Assertions.assertEquals(new Position<>(0, 2, 2), p3);
|
||||
|
||||
var p4 = ControllerRelativeCoords.translateInverse(p3, 5, 5, 5);
|
||||
Assertions.assertEquals(new Position<>(5, 7, 7), p4);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSocketAndCoordSystemTranslationToStructureDef() {
|
||||
IStructureDefinition<?> def = getDefinition();
|
||||
|
||||
// Note: the position is the same object between all of these transforms, it is never copied, its generic just
|
||||
// changes
|
||||
|
||||
var p = new Position<WorldCoords>(5, 7, 7);
|
||||
|
||||
var p2 = ControllerRelativeCoords.translate(p, 5, 5, 5);
|
||||
Assertions.assertEquals(new Position<>(0, 2, 2), p2);
|
||||
|
||||
var p3 = ExtendedFacing.NORTH_NORMAL_NONE.asCoordinateSystem().translate(p2);
|
||||
Assertions.assertEquals(new Position<>(0, -2, 2), p3);
|
||||
|
||||
var p4 = def.getCoordinateSystem("main").translate(p3);
|
||||
Assertions.assertEquals(new Position<>(2, 2, 2), p4);
|
||||
|
||||
Assertions.assertEquals(def.getSocket("main", 'X'), p4);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSocketGetters() {
|
||||
IStructureDefinition<?> def = getDefinition();
|
||||
|
||||
Assertions.assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> def.getSocket("invalid", 'X'));
|
||||
Assertions.assertThrows(
|
||||
IllegalStateException.class,
|
||||
() -> def.getSocket("main", 'Y'));
|
||||
Assertions.assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> def.getSocket("main", 'Z'));
|
||||
|
||||
Assertions.assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> def.getAllSockets("invalid", 'X'));
|
||||
|
||||
Assertions.assertEquals(
|
||||
Collections.emptyList(),
|
||||
def.getAllSockets("main", 'Z'));
|
||||
Assertions.assertEquals(
|
||||
Arrays.asList(new Position<>(2, 2, 2)),
|
||||
def.getAllSockets("main", 'X'));
|
||||
Assertions.assertEquals(
|
||||
Arrays.asList(new Position<>(2, 3, 2), new Position<>(3, 3, 2)),
|
||||
def.getAllSockets("main", 'Y'));
|
||||
}
|
||||
}
|
||||
// spotless:on
|
||||