diff --git a/README.md b/README.md
index 51fa7ba..c02592a 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,8 @@ This addon adds new features to DynamX :
- Klaxon and siren
- Fuel tank
- Immatriculation plates
-- Speed display on the vehicle
+- Speed, gear and speed limit display on the vehicle
+- Needle
### How to add modules:
@@ -71,6 +72,55 @@ FuelTank#Op{
}
```
+#### Better speed-display
+
+```
+TextInfo#Op{
+ Position: 0 0 0
+ Scale: 1 1 1
+ Rotation: 0 0 0
+ Color: 0 0 0
+ DetailToShow: GEAR
+ CarStartedReact: True
+}
+```
+
+The `DetailToShow` can be one of the following:
+ - GEAR
+ - SPEED
+ - SPEEDLIMITOR
+
+It will display the current gear, speed or speedlimitor value at the position.
+
+The `CarStartedReact` can be `True` or `False` and will enable or disable the display when the car is started.
+
+### DashboardNeedle
+
+```
+DashboardNeedle_2 {
+ Position: 0 0 0
+ Rotation: 0 0 -120
+ ObjectName: speedneedle
+ NeedleMaxTurn: 160
+ DashboardMaxValue: 6000
+ NeedleType: RPM
+}
+```
+
+| Key | Value |
+|------------------- |------------------------------------------ |
+| Rotation | Default rotation |
+| Position | Default position |
+| ObjectName | Name of the object in the 3D model |
+| NeedleMaxTurn | Max rotation in degrees on the dashboard |
+| DashboardMaxValue | Max value displayed on dashboard |
+| NeedleType | It can be `RPM` or `SPEED` |
+
+
+
+
+> Here for RPM, max DashboardMaxValue is 6000 and max NeedleMaxTurn is 160.
+
## Links
DynamX website: https://dynamx.fr
diff --git a/exemple.png b/exemple.png
new file mode 100644
index 0000000..b391d52
Binary files /dev/null and b/exemple.png differ
diff --git a/src/main/java/fr/dynamx/addons/basics/BasicsAddon.java b/src/main/java/fr/dynamx/addons/basics/BasicsAddon.java
index 5230f71..8e7ac79 100644
--- a/src/main/java/fr/dynamx/addons/basics/BasicsAddon.java
+++ b/src/main/java/fr/dynamx/addons/basics/BasicsAddon.java
@@ -4,10 +4,13 @@
import fr.dynamx.addons.basics.client.BasicsAddonController;
import fr.dynamx.addons.basics.client.InteractionKeyController;
import fr.dynamx.addons.basics.common.infos.BasicsItemInfo;
+import fr.dynamx.addons.basics.common.infos.DashboardNeedleInfo.EnumDashboardNeedleType;
+import fr.dynamx.addons.basics.common.infos.DashboardTextInfos.EnumDashboardTextType;
import fr.dynamx.addons.basics.server.CommandBasicsSpawn;
import fr.dynamx.addons.basics.utils.FuelJerrycanUtils;
import fr.dynamx.addons.basics.utils.VehicleKeyUtils;
import fr.dynamx.api.contentpack.DynamXAddon;
+import fr.dynamx.api.contentpack.registry.DefinitionType;
import fr.dynamx.common.contentpack.type.objects.ItemObject;
import fr.dynamx.common.items.DynamXItem;
import fr.dynamx.common.items.DynamXItemRegistry;
@@ -20,7 +23,6 @@
import net.minecraft.world.World;
import net.minecraftforge.fml.client.registry.ClientRegistry;
import net.minecraftforge.fml.common.FMLCommonHandler;
-import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLServerStartingEvent;
import net.minecraftforge.fml.relauncher.Side;
@@ -47,6 +49,8 @@ public static void initAddon() {
}
registerKey();
registerJerrycan();
+ new DefinitionType<>(EnumDashboardNeedleType.class, EnumDashboardNeedleType::fromString, "type.dashboardneedletype");
+ new DefinitionType<>(EnumDashboardTextType.class, EnumDashboardTextType::fromString, "type.dashboardtexttype");
}
private static void registerKey() {
diff --git a/src/main/java/fr/dynamx/addons/basics/common/infos/DashboardNeedleInfo.java b/src/main/java/fr/dynamx/addons/basics/common/infos/DashboardNeedleInfo.java
new file mode 100644
index 0000000..c303f3b
--- /dev/null
+++ b/src/main/java/fr/dynamx/addons/basics/common/infos/DashboardNeedleInfo.java
@@ -0,0 +1,146 @@
+package fr.dynamx.addons.basics.common.infos;
+
+import com.jme3.math.Vector3f;
+import fr.dynamx.addons.basics.utils.TextUtils;
+import fr.dynamx.api.contentpack.object.part.BasePart;
+import fr.dynamx.api.contentpack.object.part.IDrawablePart;
+import fr.dynamx.api.contentpack.registry.DefinitionType;
+import fr.dynamx.api.contentpack.registry.PackFileProperty;
+import fr.dynamx.api.contentpack.registry.RegisteredSubInfoType;
+import fr.dynamx.api.contentpack.registry.SubInfoTypeRegistries;
+import fr.dynamx.api.entities.VehicleEntityProperties;
+import fr.dynamx.client.renders.model.renderer.DxModelRenderer;
+import fr.dynamx.client.renders.scene.EntityRenderContext;
+import fr.dynamx.client.renders.scene.SceneBuilder;
+import fr.dynamx.client.renders.scene.SceneGraph;
+import fr.dynamx.common.contentpack.type.vehicle.ModularVehicleInfo;
+import fr.dynamx.common.entities.BaseVehicleEntity;
+import fr.dynamx.common.entities.modules.engines.CarEngineModule;
+import fr.dynamx.utils.DynamXUtils;
+import net.minecraft.client.renderer.GlStateManager;
+import org.lwjgl.opengl.GL11;
+
+import javax.annotation.Nullable;
+import java.util.List;
+import java.util.Objects;
+
+@RegisteredSubInfoType(name = "DashboardNeedle", registries = SubInfoTypeRegistries.WHEELED_VEHICLES, strictName = false)
+public class DashboardNeedleInfo extends BasePart implements IDrawablePart, ModularVehicleInfo> {
+ @PackFileProperty(configNames = "Rotation", type = DefinitionType.DynamXDefinitionTypes.VECTOR3F)
+ protected Vector3f rotation;
+
+ @PackFileProperty(configNames = "ObjectName", required = false, defaultValue = "speedneedle")
+ protected String objectName;
+
+ @PackFileProperty(configNames = "NeedleMaxTurn", required = false, defaultValue = "140", description = "common.NeedleMaxTurn")
+ protected int needleMaxTurn;
+
+ @PackFileProperty(configNames = "DashboardMaxValue", required = false, defaultValue = "140", description = "common.DashboardMaxSpeed")
+ protected int dashboardMaxValue;
+
+ @PackFileProperty(configNames = "DependsOnNode", required = false, description = "common.DependsOnNode")
+ protected String nodeDependingOnName;
+
+ @PackFileProperty(configNames = "NeedleType", required = false, defaultValue = "SPEED", description = "common.NeedleType")
+ protected EnumDashboardNeedleType needleType;
+
+ @Override
+ public String getNodeName() {
+ return getPartName();
+ }
+
+ @Override
+ public String getObjectName() {
+ return this.objectName;
+ }
+
+ @Override
+ public void addToSceneGraph(ModularVehicleInfo packInfo, SceneBuilder, ModularVehicleInfo> sceneBuilder) {
+ if (nodeDependingOnName != null) {
+ sceneBuilder.addNode(this, nodeDependingOnName, getNodeName());
+ } else {
+ sceneBuilder.addNode(this, getNodeName());
+ }
+ }
+
+ @Override
+ public SceneGraph, ModularVehicleInfo> createSceneGraph(Vector3f modelScale, List, ModularVehicleInfo>> childGraph) {
+ if (childGraph != null) throw new IllegalArgumentException("DashboardNeedleInfo can't have children parts");
+ return new SpeedNeedleNode<>(modelScale, null);
+ }
+
+ @Override
+ public String getName() {
+ return "PartShape named " + getPartName() + " in " + Objects.requireNonNull(getOwner()).getName();
+ }
+
+ public int getNeedleMaxTurn() {
+ return needleMaxTurn;
+ }
+
+ public int getDashboardMaxValue() {
+ return dashboardMaxValue;
+ }
+
+ public EnumDashboardNeedleType getNeedleType() {
+ return needleType;
+ }
+
+ public Vector3f getRotation() {
+ return rotation;
+ }
+
+ public String getNodeDependingOnName() {
+ return this.nodeDependingOnName;
+ }
+
+ public DashboardNeedleInfo(ModularVehicleInfo owner, String partName) {
+ super(owner, partName);
+ }
+
+
+ class SpeedNeedleNode, A extends ModularVehicleInfo> extends SceneGraph.Node {
+
+ public SpeedNeedleNode(Vector3f scale, List> linkedChilds) {
+ super(null, null, scale, linkedChilds);
+ }
+
+ @Override
+ public void render(@Nullable T entity, EntityRenderContext entityRenderContext, A packInfo) {
+ if (entity == null) return;
+ DxModelRenderer vehicleModel = entityRenderContext.getModel();
+ byte b = entityRenderContext.getTextureId();
+ if (entityRenderContext.getModel().containsObjectOrNode(DashboardNeedleInfo.this.getObjectName()) && entity.getModuleByType(CarEngineModule.class) != null && DashboardNeedleInfo.this.getPosition() != null) {
+ GlStateManager.pushMatrix();
+ Vector3f pos = DashboardNeedleInfo.this.getPosition();
+ GL11.glTranslatef(pos.x, pos.y, pos.z);
+ TextUtils.makeGLRotation(getRotation());
+ switch (DashboardNeedleInfo.this.getNeedleType()) {
+ case RPM:
+ int rpms = Math.round(entity.getModuleByType(CarEngineModule.class).getEngineProperty(VehicleEntityProperties.EnumEngineProperties.REVS) * 10000);
+ GlStateManager.rotate((float) DashboardNeedleInfo.this.needleMaxTurn * rpms / DashboardNeedleInfo.this.dashboardMaxValue, 0, 0, 1);
+ break;
+ case SPEED:
+ GlStateManager.rotate((float) DashboardNeedleInfo.this.needleMaxTurn * DynamXUtils.getSpeed(entity) / DashboardNeedleInfo.this.dashboardMaxValue, 0, 0, 1);
+ break;
+ }
+ vehicleModel.renderGroup(DashboardNeedleInfo.this.getObjectName(), b, entityRenderContext.isUseVanillaRender());
+ GlStateManager.popMatrix();
+ }
+ }
+ }
+
+ public enum EnumDashboardNeedleType {
+ SPEED, RPM;
+
+ public static EnumDashboardNeedleType fromString(String targetName) {
+ for (EnumDashboardNeedleType dashboardNeedleType : values()) {
+ if (dashboardNeedleType.name().equalsIgnoreCase(targetName)) {
+ return dashboardNeedleType;
+ }
+ }
+ throw new IllegalArgumentException("Invalid DashboardNeedleType value '" + targetName + "'");
+ }
+ }
+
+}
diff --git a/src/main/java/fr/dynamx/addons/basics/common/infos/DashboardTextInfos.java b/src/main/java/fr/dynamx/addons/basics/common/infos/DashboardTextInfos.java
new file mode 100644
index 0000000..550bf04
--- /dev/null
+++ b/src/main/java/fr/dynamx/addons/basics/common/infos/DashboardTextInfos.java
@@ -0,0 +1,150 @@
+package fr.dynamx.addons.basics.common.infos;
+
+import com.jme3.math.Vector3f;
+import fr.dynamx.addons.basics.BasicsAddon;
+import fr.dynamx.addons.basics.utils.TextUtils;
+import fr.dynamx.api.contentpack.object.part.BasePart;
+import fr.dynamx.api.contentpack.object.part.IDrawablePart;
+import fr.dynamx.api.contentpack.registry.DefinitionType;
+import fr.dynamx.api.contentpack.registry.PackFileProperty;
+import fr.dynamx.api.contentpack.registry.RegisteredSubInfoType;
+import fr.dynamx.api.contentpack.registry.SubInfoTypeRegistries;
+import fr.dynamx.api.entities.VehicleEntityProperties;
+import fr.dynamx.client.renders.scene.EntityRenderContext;
+import fr.dynamx.client.renders.scene.SceneBuilder;
+import fr.dynamx.client.renders.scene.SceneGraph;
+import fr.dynamx.common.contentpack.type.vehicle.ModularVehicleInfo;
+import fr.dynamx.common.entities.BaseVehicleEntity;
+import fr.dynamx.common.entities.modules.engines.CarEngineModule;
+import fr.dynamx.utils.DynamXUtils;
+
+import javax.annotation.Nullable;
+import java.util.List;
+import java.util.Objects;
+
+@RegisteredSubInfoType(name = "TextInfo", registries = {SubInfoTypeRegistries.WHEELED_VEHICLES}, strictName = false)
+public class DashboardTextInfos extends BasePart implements IDrawablePart, ModularVehicleInfo> {
+ @PackFileProperty(configNames = "Rotation", type = DefinitionType.DynamXDefinitionTypes.VECTOR3F, description = "common.rotation")
+ protected Vector3f rotation;
+
+ @PackFileProperty(configNames = "Font", required = false)
+ protected String font = BasicsAddon.ID + ":e";
+
+ @PackFileProperty(configNames = "DetailToShow", required = false, defaultValue = "GEAR")
+ protected EnumDashboardTextType detailToShow;
+ @PackFileProperty(configNames = "CarStartedReact", required = false, defaultValue = "false", type = DefinitionType.DynamXDefinitionTypes.BOOL)
+ protected boolean carStartedReact;
+
+ @PackFileProperty(configNames = "Color", description = "common.color", required = false)
+ protected int[] color = new int[]{10, 10, 10};
+
+ @PackFileProperty(configNames = "DependsOnNode", required = false, description = "common.DependsOnNode")
+ protected String nodeDependingOnName;
+
+ public DashboardTextInfos(ModularVehicleInfo owner, String partName) {
+ super(owner, partName);
+ }
+
+ @Override
+ public void appendTo(ModularVehicleInfo owner) {
+ owner.addSubProperty(this);
+ }
+
+ public Vector3f getRotation() {
+ return rotation;
+ }
+
+ public int[] getColor() {
+ return color;
+ }
+
+ public boolean isCarStartedReact() {
+ return carStartedReact;
+ }
+
+ public String getFont() {
+ return font;
+ }
+
+ public EnumDashboardTextType getDetailToShow() {
+ return detailToShow;
+ }
+
+ @Override
+ public String getName() {
+ return "PartShape named " + getPartName() + " in " + getOwner().getName();
+ }
+
+ @Override
+ public String getObjectName() {
+ return null;
+ }
+
+
+ @Override
+ public String getNodeName() {
+
+ return getPartName();
+ }
+
+ @Override
+ public void addToSceneGraph(ModularVehicleInfo packInfo, SceneBuilder, ModularVehicleInfo> sceneBuilder) {
+ if (nodeDependingOnName != null) {
+ sceneBuilder.addNode(this, nodeDependingOnName, getNodeName());
+ } else {
+ sceneBuilder.addNode(this, getNodeName());
+ }
+ }
+
+ @Override
+ public SceneGraph, ModularVehicleInfo> createSceneGraph(Vector3f modelScale, List, ModularVehicleInfo>> childGraph) {
+ if (childGraph != null) throw new IllegalArgumentException("TextInfo can't have children parts");
+ return new SpeedDisplayNode<>(modelScale, null);
+ }
+
+ class SpeedDisplayNode, A extends ModularVehicleInfo> extends SceneGraph.Node {
+ public SpeedDisplayNode(Vector3f scale, List> linkedChilds) {
+ super(null, null, scale, linkedChilds);
+ }
+
+ @Override
+ public void render(@Nullable T entity, EntityRenderContext entityRenderContext, A packInfo) {
+ if (entity == null) return;
+ if (DashboardTextInfos.this.getRotation() != null) {
+ if (entity.getModuleByType(CarEngineModule.class).getPhysicsHandler().getEngine().isStarted() && DashboardTextInfos.this.isCarStartedReact()) {
+ String value = "";
+ switch (DashboardTextInfos.this.getDetailToShow()) {
+ case GEAR:
+ float gear = entity.getModuleByType(CarEngineModule.class).getEngineProperty(VehicleEntityProperties.EnumEngineProperties.ACTIVE_GEAR);
+ if (gear == 0) value = "N";
+ else if (gear == -1) value = "R" + (int) Math.abs(gear);
+ else value = "D" + (int) gear;
+ break;
+ case SPEEDLIMITOR:
+ int tempvalue = Math.round(entity.getModuleByType(CarEngineModule.class).getSpeedLimit());
+ if (tempvalue != 0 & tempvalue < 10000000) value = String.valueOf(tempvalue);
+ else value = "";
+ break;
+ case SPEED:
+ value = String.valueOf(DynamXUtils.getSpeed(entity));
+ break;
+ }
+ TextUtils.drawText(DashboardTextInfos.this.getPosition(), DashboardTextInfos.this.getScale(), DashboardTextInfos.this.getRotation(), value, getColor(), getFont());
+ }
+ }
+ }
+ }
+
+ public enum EnumDashboardTextType {
+ SPEED, GEAR, SPEEDLIMITOR;
+
+ public static EnumDashboardTextType fromString(String targetName) {
+ for (EnumDashboardTextType dashboardNeedleType : values()) {
+ if (dashboardNeedleType.name().equalsIgnoreCase(targetName)) {
+ return dashboardNeedleType;
+ }
+ }
+ throw new IllegalArgumentException("Invalid EnumDashboardTextType value '" + targetName + "'");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/fr/dynamx/addons/basics/common/infos/SpeedDisplayInfos.java b/src/main/java/fr/dynamx/addons/basics/common/infos/SpeedDisplayInfos.java
index 575ca3e..30b7823 100644
--- a/src/main/java/fr/dynamx/addons/basics/common/infos/SpeedDisplayInfos.java
+++ b/src/main/java/fr/dynamx/addons/basics/common/infos/SpeedDisplayInfos.java
@@ -80,6 +80,7 @@ public String getNodeName() {
@Override
public void addToSceneGraph(ModularVehicleInfo packInfo, SceneBuilder, ModularVehicleInfo> sceneBuilder) {
+ if(getRotation() == null) return;
if (nodeDependingOnName != null) {
sceneBuilder.addNode(this, nodeDependingOnName, getNodeName());
} else {
@@ -89,7 +90,7 @@ public void addToSceneGraph(ModularVehicleInfo packInfo, SceneBuilder, ModularVehicleInfo> createSceneGraph(Vector3f modelScale, List, ModularVehicleInfo>> childGraph) {
- if(childGraph != null)
+ if (childGraph != null)
throw new IllegalArgumentException("SpeedDisplayInfos can't have children parts");
return new SpeedDisplayNode<>(modelScale, null);
}
@@ -105,6 +106,7 @@ public void render(@Nullable T entity, EntityRenderContext entityRenderContext,
return;
String speed = "" + DynamXUtils.getSpeed(entity);
TextUtils.drawText(SpeedDisplayInfos.this.getPosition(), SpeedDisplayInfos.this.getScale(), SpeedDisplayInfos.this.getRotation(), speed, getColor(), getFont());
+
}
}
}
\ No newline at end of file
diff --git a/src/main/java/fr/dynamx/addons/basics/utils/TextUtils.java b/src/main/java/fr/dynamx/addons/basics/utils/TextUtils.java
index e5fccc8..b879898 100644
--- a/src/main/java/fr/dynamx/addons/basics/utils/TextUtils.java
+++ b/src/main/java/fr/dynamx/addons/basics/utils/TextUtils.java
@@ -19,15 +19,7 @@ public static void drawText(Vector3f pos, Vector3f scale, Vector3f rotation, Str
GlStateManager.translate(pos.x, pos.y, pos.z);
GlStateManager.rotate(180, 1, 0, 0);
GlStateManager.rotate(180, 0, 1, 0);
- float rotate = rotation.x;
- if (rotate != 0)
- GlStateManager.rotate(rotate, 1, 0, 0);
- rotate = rotation.y;
- if (rotate != 0)
- GlStateManager.rotate(rotate, 0, 1, 0);
- rotate = rotation.z;
- if (rotate != 0)
- GlStateManager.rotate(rotate, 0, 0, 1);
+ makeGLRotation(rotation);
GlStateManager.scale(scale.x / 40, scale.y / 40, scale.z / 40);
GlStateManager.disableLighting();
@@ -45,4 +37,16 @@ public static void drawText(Vector3f pos, Vector3f scale, Vector3f rotation, Str
GlStateManager.popMatrix();
}
}
+
+ public static void makeGLRotation(Vector3f rotation) {
+ float rotate = rotation.x;
+ if (rotate != 0)
+ GlStateManager.rotate(rotate, 1, 0, 0);
+ rotate = rotation.y;
+ if (rotate != 0)
+ GlStateManager.rotate(rotate, 0, 1, 0);
+ rotate = rotation.z;
+ if (rotate != 0)
+ GlStateManager.rotate(rotate, 0, 0, 1);
+ }
}