mirror of
https://github.com/Retera/WarsmashModEngine.git
synced 2022-07-31 17:38:59 +02:00
Add game clock functionality and begin work on lighting system
This commit is contained in:
parent
cfa5f952de
commit
59d350dd9e
@ -17,7 +17,8 @@ Path06="."
|
||||
|
||||
[Map]
|
||||
//FilePath="CombatUnitTests.w3x"
|
||||
FilePath="PitchRoll.w3x"
|
||||
//FilePath="PitchRoll.w3x"
|
||||
FilePath="PeonStartingBase.w3x"
|
||||
//FilePath="PlayerPeasants.w3m"
|
||||
//FilePath="FireLord.w3x"
|
||||
//FilePath="Maps\Campaign\NightElf03.w3m"
|
@ -27,6 +27,7 @@ import com.etheller.warsmash.viewer5.SolvedPath;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxHandler;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxViewer;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.SequenceLoopMode;
|
||||
|
||||
public class WarsmashGdxGame extends ApplicationAdapter implements CanvasProvider {
|
||||
@ -62,7 +63,7 @@ public class WarsmashGdxGame extends ApplicationAdapter implements CanvasProvide
|
||||
final FolderDataSourceDescriptor currentFolder = new FolderDataSourceDescriptor(".");
|
||||
this.codebase = new CompoundDataSourceDescriptor(
|
||||
Arrays.<DataSourceDescriptor>asList(war3mpq, testingFolder, currentFolder)).createDataSource();
|
||||
this.viewer = new ModelViewer(this.codebase, this);
|
||||
this.viewer = new MdxViewer(this.codebase, this);
|
||||
|
||||
this.viewer.addHandler(new MdxHandler());
|
||||
this.viewer.enableAudio();
|
||||
|
@ -56,15 +56,13 @@ import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderUnit;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.ui.MeleeUI;
|
||||
|
||||
public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProvider, InputProcessor {
|
||||
private static final float HORIZONTAL_MAXIMUM = (float) Math.toRadians(56);
|
||||
private static final float HORIZONTAL_MINIMUM = (float) -Math.toRadians(56);
|
||||
private static final double HORIZONTAL_ANGLE_INCREMENT = Math.PI / 60;
|
||||
|
||||
private static final Vector3 clickLocationTemp = new Vector3();
|
||||
private static final Vector2 clickLocationTemp2 = new Vector2();
|
||||
private DataSource codebase;
|
||||
private War3MapViewer viewer;
|
||||
private CameraManager cameraManager;
|
||||
private GameCameraManager cameraManager;
|
||||
private final Rectangle tempRect = new Rectangle();
|
||||
|
||||
// libGDX stuff
|
||||
@ -146,7 +144,20 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
this.cameraManager = new CameraManager();
|
||||
final Element cameraData = this.viewer.miscData.get("Camera");
|
||||
final Element cameraListenerData = this.viewer.miscData.get("Listener");
|
||||
final CameraPreset[] cameraPresets = new CameraPreset[6];
|
||||
for (int i = 0; i < cameraPresets.length; i++) {
|
||||
cameraPresets[i] = new CameraPreset(cameraData.getFieldFloatValue("AOA", i),
|
||||
cameraData.getFieldFloatValue("FOV", i), cameraData.getFieldFloatValue("Rotation", i),
|
||||
cameraData.getFieldFloatValue("Rotation", i + cameraPresets.length),
|
||||
cameraData.getFieldFloatValue("Rotation", i + (cameraPresets.length * 2)),
|
||||
cameraData.getFieldFloatValue("Distance", i), cameraData.getFieldFloatValue("FarZ", i),
|
||||
cameraData.getFieldFloatValue("NearZ", i), cameraData.getFieldFloatValue("Height", i),
|
||||
cameraListenerData.getFieldFloatValue("ListenerDistance", i),
|
||||
cameraListenerData.getFieldFloatValue("ListenerAOA", i));
|
||||
}
|
||||
this.cameraManager = new GameCameraManager(cameraPresets);
|
||||
|
||||
this.cameraManager.setupCamera(this.viewer.worldScene);
|
||||
|
||||
@ -287,9 +298,10 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
||||
final float deltaTime = Gdx.graphics.getDeltaTime();
|
||||
Gdx.gl30.glBindVertexArray(WarsmashGdxGame.VAO);
|
||||
this.cameraManager.target.add(this.cameraVelocity.x * deltaTime, this.cameraVelocity.y * deltaTime, 0);
|
||||
this.cameraManager.target.z = Math.max(
|
||||
this.cameraManager.target.z = (Math.max(
|
||||
this.viewer.terrain.getGroundHeight(this.cameraManager.target.x, this.cameraManager.target.y),
|
||||
this.viewer.terrain.getWaterHeight(this.cameraManager.target.x, this.cameraManager.target.y)) - 256;
|
||||
this.viewer.terrain.getWaterHeight(this.cameraManager.target.x, this.cameraManager.target.y)) - 256)
|
||||
+ this.cameraManager.presets[this.cameraManager.currentPreset].height + 256;
|
||||
this.cameraManager.updateCamera();
|
||||
this.meleeUI.updatePortrait();
|
||||
this.viewer.updateAndRender();
|
||||
@ -400,28 +412,26 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
||||
this.meleeUI.resize();
|
||||
}
|
||||
|
||||
public static class CameraManager {
|
||||
private final float[] cameraPositionTemp = new float[3];
|
||||
private final float[] cameraTargetTemp = new float[3];
|
||||
public com.etheller.warsmash.viewer5.handlers.mdx.Camera modelCamera;
|
||||
private MdxComplexInstance modelInstance;
|
||||
private CanvasProvider canvas;
|
||||
private Camera camera;
|
||||
private float moveSpeed;
|
||||
private float rotationSpeed;
|
||||
private float zoomFactor;
|
||||
private float horizontalAngle;
|
||||
private float verticalAngle;
|
||||
private float verticalAngleAcceleration;
|
||||
private float distance;
|
||||
private Vector3 position;
|
||||
private Vector3 target;
|
||||
private Vector3 worldUp;
|
||||
private Vector3 vecHeap;
|
||||
private Quaternion quatHeap;
|
||||
private Quaternion quatHeap2;
|
||||
private boolean insertDown;
|
||||
private boolean deleteDown;
|
||||
public static abstract class CameraManager {
|
||||
protected final float[] cameraPositionTemp = new float[3];
|
||||
protected final float[] cameraTargetTemp = new float[3];
|
||||
protected CanvasProvider canvas;
|
||||
protected Camera camera;
|
||||
protected float moveSpeed;
|
||||
protected float rotationSpeed;
|
||||
protected float zoomFactor;
|
||||
protected float horizontalAngle;
|
||||
protected float verticalAngle;
|
||||
protected float distance;
|
||||
protected Vector3 position;
|
||||
protected Vector3 target;
|
||||
protected Vector3 worldUp;
|
||||
protected Vector3 vecHeap;
|
||||
protected Quaternion quatHeap;
|
||||
protected Quaternion quatHeap2;
|
||||
|
||||
public CameraManager() {
|
||||
}
|
||||
|
||||
// An orbit camera setup example.
|
||||
// Left mouse button controls the orbit itself.
|
||||
@ -449,45 +459,58 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
||||
// cameraUpdate();
|
||||
}
|
||||
|
||||
public abstract void updateCamera();
|
||||
|
||||
// private void cameraUpdate() {
|
||||
//
|
||||
// }
|
||||
}
|
||||
|
||||
public static final class GameCameraManager extends CameraManager {
|
||||
private final CameraPreset[] presets;
|
||||
private int currentPreset = 0;
|
||||
|
||||
protected boolean insertDown;
|
||||
protected boolean deleteDown;
|
||||
|
||||
public GameCameraManager(final CameraPreset[] presets) {
|
||||
this.presets = presets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCamera() {
|
||||
this.quatHeap2.idt();
|
||||
final CameraPreset cameraPreset = this.presets[this.currentPreset];
|
||||
this.quatHeap.idt();
|
||||
this.horizontalAngle = (float) Math
|
||||
.toRadians(cameraPreset.getRotation(this.insertDown, this.deleteDown) - 90);
|
||||
this.quatHeap.setFromAxisRad(0, 0, 1, this.horizontalAngle);
|
||||
if (this.insertDown && !this.deleteDown) {
|
||||
this.horizontalAngle -= HORIZONTAL_ANGLE_INCREMENT;
|
||||
if (this.horizontalAngle < HORIZONTAL_MINIMUM) {
|
||||
this.horizontalAngle = HORIZONTAL_MINIMUM;
|
||||
}
|
||||
}
|
||||
else if (this.deleteDown && !this.insertDown) {
|
||||
this.horizontalAngle += HORIZONTAL_ANGLE_INCREMENT;
|
||||
if (this.horizontalAngle > HORIZONTAL_MAXIMUM) {
|
||||
this.horizontalAngle = HORIZONTAL_MAXIMUM;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (Math.abs(this.horizontalAngle) < HORIZONTAL_ANGLE_INCREMENT) {
|
||||
this.horizontalAngle = 0;
|
||||
}
|
||||
else {
|
||||
this.horizontalAngle -= HORIZONTAL_ANGLE_INCREMENT * Math.signum(this.horizontalAngle);
|
||||
this.distance = Math.max(1200, cameraPreset.distance);
|
||||
this.verticalAngle = (float) Math.toRadians(Math.min(335, cameraPreset.aoa) - 270);
|
||||
this.quatHeap2.setFromAxisRad(1, 0, 0, this.verticalAngle);
|
||||
this.quatHeap.mul(this.quatHeap2);
|
||||
|
||||
this.position.set(0, 0, 1);
|
||||
this.quatHeap.transform(this.position);
|
||||
this.position.nor();
|
||||
this.position.scl(this.distance);
|
||||
this.position = this.position.add(this.target);
|
||||
this.camera.perspective((float) Math.toRadians(cameraPreset.fov / 2), this.camera.getAspect(),
|
||||
cameraPreset.nearZ, cameraPreset.farZ);
|
||||
|
||||
this.camera.moveToAndFace(this.position, this.target, this.worldUp);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class PortraitCameraManager extends CameraManager {
|
||||
public com.etheller.warsmash.viewer5.handlers.mdx.Camera modelCamera;
|
||||
protected MdxComplexInstance modelInstance;
|
||||
|
||||
@Override
|
||||
public void updateCamera() {
|
||||
this.quatHeap.idt();
|
||||
this.quatHeap.setFromAxisRad(0, 0, 1, this.horizontalAngle);
|
||||
this.quatHeap2.idt();
|
||||
this.verticalAngle += this.verticalAngleAcceleration;
|
||||
this.verticalAngleAcceleration *= 0.975f;
|
||||
if (this.verticalAngle > ((Math.PI / 2) - Math.toRadians(17))) {
|
||||
this.verticalAngle = (float) ((Math.PI / 2) - Math.toRadians(17));
|
||||
if (this.verticalAngleAcceleration > 0) {
|
||||
this.verticalAngleAcceleration = 0;
|
||||
}
|
||||
}
|
||||
if (this.verticalAngle < (float) Math.toRadians(34)) {
|
||||
this.verticalAngle = (float) Math.toRadians(34);
|
||||
if (this.verticalAngleAcceleration < 0) {
|
||||
this.verticalAngleAcceleration = 0;
|
||||
}
|
||||
}
|
||||
this.quatHeap2.setFromAxisRad(1, 0, 0, this.verticalAngle);
|
||||
this.quatHeap.mul(this.quatHeap2);
|
||||
|
||||
@ -525,10 +548,6 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
||||
this.modelCamera = portraitModel.getCameras().get(0);
|
||||
}
|
||||
}
|
||||
|
||||
// private void cameraUpdate() {
|
||||
//
|
||||
// }
|
||||
}
|
||||
|
||||
private final float cameraSpeed = 4096.0f; // per second
|
||||
@ -697,12 +716,12 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
||||
|
||||
@Override
|
||||
public boolean scrolled(final int amount) {
|
||||
this.cameraManager.verticalAngleAcceleration -= amount / 100.f;
|
||||
if (this.cameraManager.verticalAngleAcceleration > (Math.PI / 128)) {
|
||||
this.cameraManager.verticalAngleAcceleration = (float) (Math.PI / 128);
|
||||
this.cameraManager.currentPreset -= amount;
|
||||
if (this.cameraManager.currentPreset < 0) {
|
||||
this.cameraManager.currentPreset = 0;
|
||||
}
|
||||
if (this.cameraManager.verticalAngleAcceleration < (-Math.PI / 128)) {
|
||||
this.cameraManager.verticalAngleAcceleration = -(float) (Math.PI / 128);
|
||||
if (this.cameraManager.currentPreset >= this.cameraManager.presets.length) {
|
||||
this.cameraManager.currentPreset = this.cameraManager.presets.length - 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -716,4 +735,44 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
||||
this.text = text;
|
||||
}
|
||||
}
|
||||
|
||||
private static class CameraPreset {
|
||||
private final float aoa;
|
||||
private final float fov;
|
||||
private final float rotation;
|
||||
private final float rotationInsert;
|
||||
private final float rotationDelete;
|
||||
private final float distance;
|
||||
private final float farZ;
|
||||
private final float nearZ;
|
||||
private final float height;
|
||||
private final float listenerDistance;
|
||||
private final float listenerAOA;
|
||||
|
||||
public CameraPreset(final float aoa, final float fov, final float rotation, final float rotationInsert,
|
||||
final float rotationDelete, final float distance, final float farZ, final float nearZ,
|
||||
final float height, final float listenerDistance, final float listenerAOA) {
|
||||
this.aoa = aoa;
|
||||
this.fov = fov;
|
||||
this.rotation = rotation;
|
||||
this.rotationInsert = rotationInsert;
|
||||
this.rotationDelete = rotationDelete;
|
||||
this.distance = distance;
|
||||
this.farZ = farZ;
|
||||
this.nearZ = nearZ;
|
||||
this.height = height;
|
||||
this.listenerDistance = listenerDistance;
|
||||
this.listenerAOA = listenerAOA;
|
||||
}
|
||||
|
||||
public float getRotation(final boolean insertDown, final boolean deleteDown) {
|
||||
if (insertDown && !deleteDown) {
|
||||
return this.rotationInsert;
|
||||
}
|
||||
if (!insertDown && deleteDown) {
|
||||
return this.rotationDelete;
|
||||
}
|
||||
return this.rotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -209,7 +209,6 @@ public final class GameUI extends AbstractUIFrame implements UIFrame {
|
||||
final MdxModel model = (MdxModel) this.modelViewer.load(backgroundArt, this.modelViewer.mapPathSolver,
|
||||
this.modelViewer.solverParams);
|
||||
spriteFrame.setModel(model);
|
||||
spriteFrame.setSequence(0);
|
||||
viewport2 = this.fdfCoordinateResolutionDummyViewport;
|
||||
inflatedFrame = spriteFrame;
|
||||
}
|
||||
|
@ -50,4 +50,22 @@ public class SpriteFrame extends AbstractRenderableFrame {
|
||||
}
|
||||
}
|
||||
|
||||
public void setAnimationSpeed(final float speedRatio) {
|
||||
if (this.instance != null) {
|
||||
this.instance.setAnimationSpeed(speedRatio);
|
||||
}
|
||||
}
|
||||
|
||||
public void setFrame(final int animationFrame) {
|
||||
if (this.instance != null) {
|
||||
this.instance.setFrame(animationFrame);
|
||||
}
|
||||
}
|
||||
|
||||
public void setFrameByRatio(final float ratioOfAnimationCompleted) {
|
||||
if (this.instance != null) {
|
||||
this.instance.setFrameByRatio(ratioOfAnimationCompleted);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,6 +20,8 @@ public interface GameObject {
|
||||
|
||||
public float getFieldFloatValue(String field);
|
||||
|
||||
public float getFieldFloatValue(String field, int index);
|
||||
|
||||
public List<? extends GameObject> getFieldAsList(String field, ObjectData objectData);
|
||||
|
||||
public String getId();
|
||||
@ -75,6 +77,11 @@ public interface GameObject {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFieldFloatValue(final String field, final int index) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends GameObject> getFieldAsList(final String field, final ObjectData objectData) {
|
||||
return Collections.emptyList();
|
||||
|
@ -79,6 +79,18 @@ public abstract class HashedGameObject implements GameObject {
|
||||
return i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFieldFloatValue(final String field, final int index) {
|
||||
float i = 0;
|
||||
try {
|
||||
i = Float.parseFloat(getField(field, index));
|
||||
}
|
||||
catch (final NumberFormatException e) {
|
||||
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setField(final String field, final String value, final int index) {
|
||||
final StringKey key = new StringKey(field);
|
||||
|
@ -504,6 +504,17 @@ public class StandardObjectData {
|
||||
return 0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFieldFloatValue(final String field, final int index) {
|
||||
for (final DataTable table : this.dataSource.getTables()) {
|
||||
final Element element = table.get(this.id);
|
||||
if ((element != null) && element.hasField(field)) {
|
||||
return element.getFieldFloatValue(field, index);
|
||||
}
|
||||
}
|
||||
return 0f;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc) I'm not entirely sure this is still safe to use
|
||||
*
|
||||
|
@ -8,10 +8,12 @@ import java.nio.IntBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
import java.util.List;
|
||||
|
||||
import com.badlogic.gdx.math.Intersector;
|
||||
import com.badlogic.gdx.math.Matrix4;
|
||||
import com.badlogic.gdx.math.Quaternion;
|
||||
import com.badlogic.gdx.math.Rectangle;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import com.badlogic.gdx.math.collision.Ray;
|
||||
|
||||
public enum RenderMathUtils {
|
||||
;
|
||||
@ -451,6 +453,128 @@ public enum RenderMathUtils {
|
||||
return out;
|
||||
}
|
||||
|
||||
static Vector3 best = new Vector3();
|
||||
static Vector3 tmp = new Vector3();
|
||||
static Vector3 tmp1 = new Vector3();
|
||||
static Vector3 tmp2 = new Vector3();
|
||||
static Vector3 tmp3 = new Vector3();
|
||||
|
||||
/**
|
||||
* Intersects the given ray with list of triangles. Returns the nearest
|
||||
* intersection point in intersection
|
||||
*
|
||||
* @param ray The ray
|
||||
* @param vertices the vertices
|
||||
* @param indices the indices, each successive 3 shorts index the 3
|
||||
* vertices of a triangle
|
||||
* @param vertexSize the size of a vertex in floats
|
||||
* @param intersection The nearest intersection point (optional)
|
||||
* @return Whether the ray and the triangles intersect.
|
||||
*/
|
||||
public static boolean intersectRayTriangles(final Ray ray, final float[] vertices, final int[] indices,
|
||||
final int vertexSize, final Vector3 intersection) {
|
||||
float min_dist = Float.MAX_VALUE;
|
||||
boolean hit = false;
|
||||
|
||||
if ((indices.length % 3) != 0) {
|
||||
throw new RuntimeException("triangle list size is not a multiple of 3");
|
||||
}
|
||||
|
||||
for (int i = 0; i < indices.length; i += 3) {
|
||||
final int i1 = indices[i] * vertexSize;
|
||||
final int i2 = indices[i + 1] * vertexSize;
|
||||
final int i3 = indices[i + 2] * vertexSize;
|
||||
|
||||
final boolean result = Intersector.intersectRayTriangle(ray,
|
||||
tmp1.set(vertices[i1], vertices[i1 + 1], vertices[i1 + 2]),
|
||||
tmp2.set(vertices[i2], vertices[i2 + 1], vertices[i2 + 2]),
|
||||
tmp3.set(vertices[i3], vertices[i3 + 1], vertices[i3 + 2]), tmp);
|
||||
|
||||
if (result == true) {
|
||||
final float dist = ray.origin.dst2(tmp);
|
||||
if (dist < min_dist) {
|
||||
min_dist = dist;
|
||||
best.set(tmp);
|
||||
hit = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hit == false) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
if (intersection != null) {
|
||||
intersection.set(best);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Intersects the given ray with list of triangles. Returns the nearest
|
||||
* intersection point in intersection
|
||||
*
|
||||
* @param ray The ray
|
||||
* @param vertices the vertices
|
||||
* @param indices the indices, each successive 3 shorts index the 3
|
||||
* vertices of a triangle
|
||||
* @param vertexSize the size of a vertex in floats
|
||||
* @param intersection The nearest intersection point (optional)
|
||||
* @return Whether the ray and the triangles intersect.
|
||||
*/
|
||||
public static boolean intersectRayTriangles(final Ray ray, final float[] vertices, final int[] indices,
|
||||
final int vertexSize, final Vector3 worldLocation, final float facingRadians, final Vector3 intersection) {
|
||||
float min_dist = Float.MAX_VALUE;
|
||||
boolean hit = false;
|
||||
|
||||
if ((indices.length % 3) != 0) {
|
||||
throw new RuntimeException("triangle list size is not a multiple of 3");
|
||||
}
|
||||
|
||||
final float facingX_X = (float) Math.cos(facingRadians);
|
||||
final float facingX_Y = (float) Math.sin(facingRadians);
|
||||
final double halfPi = Math.PI / 2;
|
||||
final float facingY_X = (float) Math.cos(facingRadians + halfPi);
|
||||
final float facingY_Y = (float) Math.sin(facingRadians + halfPi);
|
||||
for (int i = 0; i < indices.length; i += 3) {
|
||||
final int i1 = indices[i] * vertexSize;
|
||||
final int i2 = indices[i + 1] * vertexSize;
|
||||
final int i3 = indices[i + 2] * vertexSize;
|
||||
|
||||
final boolean result = Intersector.intersectRayTriangle(ray,
|
||||
tmp1.set((vertices[i1] * facingX_X) + (vertices[i1 + 1] * facingY_X) + worldLocation.x,
|
||||
(vertices[i1] * facingX_Y) + (vertices[i1 + 1] * facingY_Y) + worldLocation.y,
|
||||
vertices[i1 + 2] + worldLocation.z),
|
||||
tmp2.set((vertices[i2] * facingX_X) + (vertices[i2 + 1] * facingY_X) + worldLocation.x,
|
||||
(vertices[i2] * facingX_Y) + (vertices[i2 + 1] * facingY_Y) + worldLocation.y,
|
||||
vertices[i2 + 2] + worldLocation.z),
|
||||
tmp3.set((vertices[i3] * facingX_X) + (vertices[i3 + 1] * facingY_X) + worldLocation.x,
|
||||
(vertices[i3] * facingX_Y) + (vertices[i3 + 1] * facingY_Y) + worldLocation.y,
|
||||
vertices[i3 + 2] + worldLocation.z),
|
||||
tmp);
|
||||
|
||||
if (result == true) {
|
||||
final float dist = ray.origin.dst2(tmp);
|
||||
if (dist < min_dist) {
|
||||
min_dist = dist;
|
||||
best.set(tmp);
|
||||
hit = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hit == false) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
if (intersection != null) {
|
||||
intersection.set(best);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// ==== All of the following "wrap" calls are horribly inefficient. Eventually
|
||||
// they should be removed entirely with better design.
|
||||
// Until that happens, be sure to only call them during setup and not while the
|
||||
|
@ -77,9 +77,13 @@ public abstract class ModelInstance extends Node {
|
||||
}
|
||||
}
|
||||
|
||||
this.updateLights(scene);
|
||||
|
||||
this.updateFrame = this.model.viewer.frame;
|
||||
}
|
||||
|
||||
protected abstract void updateLights(Scene scene2);
|
||||
|
||||
public boolean setScene(final Scene scene) {
|
||||
return scene.addInstance(this);
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ import com.etheller.warsmash.viewer5.gl.WebGL;
|
||||
import com.etheller.warsmash.viewer5.handlers.ResourceHandler;
|
||||
import com.etheller.warsmash.viewer5.handlers.ResourceHandlerConstructionParams;
|
||||
|
||||
public class ModelViewer {
|
||||
public abstract class ModelViewer {
|
||||
public DataSource dataSource;
|
||||
public final CanvasProvider canvas;
|
||||
public List<Resource> resources;
|
||||
@ -366,4 +366,6 @@ public class ModelViewer {
|
||||
private void onResourceLoadError() {
|
||||
System.err.println("error, this, InvalidHandler, FailedToLoad");
|
||||
}
|
||||
|
||||
public abstract SceneLightManager createLightManager();
|
||||
}
|
||||
|
@ -58,6 +58,7 @@ public abstract class Scene {
|
||||
* If true, alpha works as usual.
|
||||
*/
|
||||
public boolean alpha = false;
|
||||
private final SceneLightManager lightManager;
|
||||
|
||||
public Scene(final ModelViewer viewer) {
|
||||
final CanvasProvider canvas = viewer.canvas;
|
||||
@ -85,6 +86,8 @@ public abstract class Scene {
|
||||
this.instanceDepthComparator = new InstanceDepthComparator();
|
||||
this.visibleCells = 0;
|
||||
this.visibleInstances = 0;
|
||||
|
||||
this.lightManager = this.viewer.createLightManager();
|
||||
}
|
||||
|
||||
public boolean enableAudio() {
|
||||
@ -300,6 +303,14 @@ public abstract class Scene {
|
||||
}
|
||||
}
|
||||
|
||||
public void addLight(final SceneLightInstance lightInstance) {
|
||||
this.lightManager.add(lightInstance);
|
||||
}
|
||||
|
||||
public void removeLight(final SceneLightInstance lightInstance) {
|
||||
this.lightManager.remove(lightInstance);
|
||||
}
|
||||
|
||||
private static final class InstanceDepthComparator implements Comparator<ModelInstance> {
|
||||
@Override
|
||||
public int compare(final ModelInstance o1, final ModelInstance o2) {
|
||||
|
@ -0,0 +1,5 @@
|
||||
package com.etheller.warsmash.viewer5;
|
||||
|
||||
public interface SceneLightInstance {
|
||||
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.etheller.warsmash.viewer5;
|
||||
|
||||
public interface SceneLightManager {
|
||||
public void add(final SceneLightInstance lightInstance);
|
||||
|
||||
public void remove(final SceneLightInstance lightInstance);
|
||||
}
|
@ -5,6 +5,8 @@ import com.badlogic.gdx.math.Matrix4;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import com.badlogic.gdx.math.collision.BoundingBox;
|
||||
import com.badlogic.gdx.math.collision.Ray;
|
||||
import com.etheller.warsmash.util.RenderMathUtils;
|
||||
import com.etheller.warsmash.viewer5.GenericNode;
|
||||
|
||||
public class CollisionShape extends GenericObject {
|
||||
private static Vector3 intersectHeap = new Vector3();
|
||||
@ -87,4 +89,22 @@ public class CollisionShape extends GenericObject {
|
||||
return Intersector.intersectRaySphere(ray, intersectHeap, this.radius, intersection);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean intersectRayTriangles(final Ray ray, final GenericNode mdxNode, final float[] vertices,
|
||||
final int[] indices, final int vertexSize, final Vector3 intersection) {
|
||||
intersectMatrixHeap.set(mdxNode.worldMatrix);
|
||||
Matrix4.inv(intersectMatrixHeap.val);
|
||||
intersectHeap.set(ray.origin);
|
||||
intersectHeap2.set(ray.direction);
|
||||
intersectHeap2.add(ray.origin);
|
||||
intersectHeap.prj(intersectMatrixHeap);
|
||||
intersectHeap2.prj(intersectMatrixHeap);
|
||||
intersectHeap2.sub(intersectHeap);
|
||||
intersectRayHeap.set(intersectHeap, intersectHeap2);
|
||||
if (RenderMathUtils.intersectRayTriangles(intersectRayHeap, vertices, indices, vertexSize, intersection)) {
|
||||
intersection.prj(mdxNode.worldMatrix);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -24,10 +24,13 @@ public class Geoset {
|
||||
private final int openGLSkinType;
|
||||
private final int skinStride;
|
||||
private final int boneCountOffsetBytes;
|
||||
public final boolean unselectable;
|
||||
public final com.etheller.warsmash.parsers.mdlx.Geoset mdlxGeoset;
|
||||
|
||||
public Geoset(final MdxModel model, final int index, final int positionOffset, final int normalOffset,
|
||||
final int uvOffset, final int skinOffset, final int faceOffset, final int vertices, final int elements,
|
||||
final int openGLSkinType, final int skinStride, final int boneCountOffsetBytes) {
|
||||
final int openGLSkinType, final int skinStride, final int boneCountOffsetBytes, final boolean unselectable,
|
||||
final com.etheller.warsmash.parsers.mdlx.Geoset mdlxGeoset) {
|
||||
this.model = model;
|
||||
this.index = index;
|
||||
this.positionOffset = positionOffset;
|
||||
@ -40,6 +43,8 @@ public class Geoset {
|
||||
this.openGLSkinType = openGLSkinType;
|
||||
this.skinStride = skinStride;
|
||||
this.boneCountOffsetBytes = boneCountOffsetBytes;
|
||||
this.unselectable = unselectable;
|
||||
this.mdlxGeoset = mdlxGeoset;
|
||||
|
||||
for (final GeosetAnimation geosetAnimation : model.getGeosetAnimations()) {
|
||||
if (geosetAnimation.geosetId == index) {
|
||||
|
@ -4,7 +4,7 @@ import com.etheller.warsmash.parsers.mdlx.AnimationMap;
|
||||
|
||||
public class Light extends GenericObject {
|
||||
|
||||
private final int type;
|
||||
private final Type type;
|
||||
private final float[] attenuation;
|
||||
private final float[] color;
|
||||
private final float intensity;
|
||||
@ -14,7 +14,18 @@ public class Light extends GenericObject {
|
||||
public Light(final MdxModel model, final com.etheller.warsmash.parsers.mdlx.Light light, final int index) {
|
||||
super(model, light, index);
|
||||
|
||||
this.type = light.getType();
|
||||
switch (light.getType()) {
|
||||
case 0:
|
||||
this.type = Type.OMNIDIRECTIONAL;
|
||||
break;
|
||||
case 2:
|
||||
this.type = Type.DIRECTIONAL;
|
||||
break;
|
||||
default:
|
||||
case 1:
|
||||
this.type = Type.AMBIENT;
|
||||
break;
|
||||
}
|
||||
this.attenuation = light.getAttenuation();
|
||||
this.color = light.getColor();
|
||||
this.intensity = light.getIntensity();
|
||||
@ -22,6 +33,10 @@ public class Light extends GenericObject {
|
||||
this.ambientIntensity = light.getAmbientIntensity();
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public int getAttenuationStart(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getScalarValue(out, AnimationMap.KLAS.getWar3id(), sequence, frame, counter, this.attenuation[0]);
|
||||
}
|
||||
@ -45,4 +60,15 @@ public class Light extends GenericObject {
|
||||
public int getAmbientColor(final float[] out, final int sequence, final int frame, final int counter) {
|
||||
return this.getVectorValue(out, AnimationMap.KLBC.getWar3id(), sequence, frame, counter, this.ambientColor);
|
||||
}
|
||||
|
||||
public static enum Type {
|
||||
// Omnidirectional light used for in-game sun
|
||||
OMNIDIRECTIONAL,
|
||||
// Directional light used for torches in the game world, and similar objects
|
||||
// that "glow"
|
||||
DIRECTIONAL,
|
||||
// Directional ambient light used for torches in the game world, and similar
|
||||
// objects that "glow"
|
||||
AMBIENT;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,78 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import com.etheller.warsmash.viewer5.Scene;
|
||||
import com.etheller.warsmash.viewer5.SceneLightInstance;
|
||||
import com.etheller.warsmash.viewer5.UpdatableObject;
|
||||
|
||||
public class LightInstance implements UpdatableObject, SceneLightInstance {
|
||||
private static final float[] vectorHeap = new float[3];
|
||||
private static final float[] scalarHeap = new float[1];
|
||||
protected final MdxNode node;
|
||||
protected final Light light;
|
||||
private boolean visible;
|
||||
private boolean loadedInScene;
|
||||
|
||||
public LightInstance(final MdxComplexInstance instance, final Light light) {
|
||||
this.node = instance.nodes[light.index];
|
||||
this.light = light;
|
||||
}
|
||||
|
||||
public void bind(final int offset, final FloatBuffer floatBuffer, final int sequence, final int frame,
|
||||
final int counter) {
|
||||
this.light.getAttenuationStart(scalarHeap, sequence, frame, counter);
|
||||
final float attenuationStart = scalarHeap[0];
|
||||
this.light.getAttenuationEnd(scalarHeap, sequence, frame, counter);
|
||||
final float attenuationEnd = scalarHeap[0];
|
||||
this.light.getIntensity(scalarHeap, sequence, frame, counter);
|
||||
final float intensity = scalarHeap[0];
|
||||
this.light.getColor(vectorHeap, sequence, frame, counter);
|
||||
final float colorRed = vectorHeap[0];
|
||||
final float colorGreen = vectorHeap[1];
|
||||
final float colorBlue = vectorHeap[2];
|
||||
this.light.getAmbientIntensity(scalarHeap, sequence, frame, counter);
|
||||
final float ambientIntensity = scalarHeap[0];
|
||||
this.light.getAmbientColor(vectorHeap, sequence, frame, counter);
|
||||
final float ambientColorRed = vectorHeap[0];
|
||||
final float ambientColorGreen = vectorHeap[1];
|
||||
final float ambientColorBlue = vectorHeap[2];
|
||||
floatBuffer.put(offset, this.node.worldLocation.x);
|
||||
floatBuffer.put(offset + 1, this.node.worldLocation.y);
|
||||
floatBuffer.put(offset + 2, this.node.worldLocation.z);
|
||||
// I use some padding to make the memory structure of the light be a 4x4 float
|
||||
// grid, when somebody who actually has experience with this stuff comes along
|
||||
// to change this to something smart, maybe they'll remove the padding if it's
|
||||
// not necessary. I'm basing how I implement this on how Ghostwolf did
|
||||
// BoneTexture
|
||||
floatBuffer.put(offset + 3, 0);
|
||||
floatBuffer.put(offset + 4, this.light.getType().ordinal());
|
||||
floatBuffer.put(offset + 5, attenuationStart);
|
||||
floatBuffer.put(offset + 6, attenuationEnd);
|
||||
floatBuffer.put(offset + 7, 0);
|
||||
floatBuffer.put(offset + 8, colorRed);
|
||||
floatBuffer.put(offset + 9, colorGreen);
|
||||
floatBuffer.put(offset + 10, colorBlue);
|
||||
floatBuffer.put(offset + 11, intensity);
|
||||
floatBuffer.put(offset + 12, ambientColorRed);
|
||||
floatBuffer.put(offset + 13, ambientColorGreen);
|
||||
floatBuffer.put(offset + 14, ambientColorBlue);
|
||||
floatBuffer.put(offset + 15, ambientIntensity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(final float dt, final boolean visible) {
|
||||
this.visible = visible;
|
||||
}
|
||||
|
||||
public void update(final Scene scene) {
|
||||
if (this.loadedInScene != this.visible) {
|
||||
if (this.visible) {
|
||||
scene.addLight(this);
|
||||
}
|
||||
else {
|
||||
scene.removeLight(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -34,6 +34,7 @@ public class MdxComplexInstance extends ModelInstance {
|
||||
private static final float[] alphaHeap = new float[1];
|
||||
private static final long[] textureIdHeap = new long[1];
|
||||
|
||||
public List<LightInstance> lights = new ArrayList<>();
|
||||
public List<AttachmentInstance> attachments = new ArrayList<>();
|
||||
public List<ParticleEmitter> particleEmitters = new ArrayList<>();
|
||||
public List<ParticleEmitter2> particleEmitters2 = new ArrayList<>();
|
||||
@ -106,7 +107,9 @@ public class MdxComplexInstance extends ModelInstance {
|
||||
}
|
||||
|
||||
for (final Light light : model.lights) {
|
||||
this.initNode(this.nodes, this.nodes[nodeIndex++], light);
|
||||
final LightInstance lightInstance = new LightInstance(this, light);
|
||||
this.lights.add(lightInstance);
|
||||
this.initNode(this.nodes, this.nodes[nodeIndex++], light, lightInstance);
|
||||
}
|
||||
|
||||
for (final Helper helper : model.helpers) {
|
||||
@ -601,6 +604,13 @@ public class MdxComplexInstance extends ModelInstance {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateLights(final Scene scene) {
|
||||
for (final LightInstance light : this.lights) {
|
||||
light.update(scene);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the team color of this instance.
|
||||
*/
|
||||
@ -715,16 +725,47 @@ public class MdxComplexInstance extends ModelInstance {
|
||||
* @param ray
|
||||
*/
|
||||
public boolean intersectRayWithCollision(final Ray ray, final Vector3 intersection) {
|
||||
for (final CollisionShape collisionShape : ((MdxModel) this.model).collisionShapes) {
|
||||
final MdxModel mdxModel = (MdxModel) this.model;
|
||||
final List<CollisionShape> collisionShapes = mdxModel.collisionShapes;
|
||||
if (collisionShapes.isEmpty()) {
|
||||
for (final Geoset geoset : mdxModel.geosets) {
|
||||
if (!geoset.unselectable) {
|
||||
geoset.getAlpha(alphaHeap, this.sequence, this.frame, this.counter);
|
||||
if (alphaHeap[0] > 0) {
|
||||
final com.etheller.warsmash.parsers.mdlx.Geoset mdlxGeoset = geoset.mdlxGeoset;
|
||||
if (CollisionShape.intersectRayTriangles(ray, this, mdlxGeoset.getVertices(),
|
||||
mdlxGeoset.getFaces(), 3, intersection)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (final CollisionShape collisionShape : collisionShapes) {
|
||||
final MdxNode mdxNode = this.nodes[collisionShape.index];
|
||||
if (collisionShape.checkIntersect(ray, mdxNode, intersection)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setAnimationSpeed(final float speedRatio) {
|
||||
this.animationSpeed = speedRatio;
|
||||
}
|
||||
|
||||
public void setFrame(final int frame) {
|
||||
this.frame = frame;
|
||||
this.floatingFrame = frame;
|
||||
}
|
||||
|
||||
public void setFrameByRatio(final float ratioOfAnimationCompleted) {
|
||||
final Sequence currentlyPlayingSequence = ((MdxModel) this.model).sequences.get(this.sequence);
|
||||
this.floatingFrame = currentlyPlayingSequence.getInterval()[0]
|
||||
+ ((currentlyPlayingSequence.getInterval()[1] - currentlyPlayingSequence.getInterval()[0])
|
||||
* ratioOfAnimationCompleted);
|
||||
this.frame = (int) this.floatingFrame;
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import com.etheller.warsmash.viewer5.BatchedInstance;
|
||||
import com.etheller.warsmash.viewer5.Model;
|
||||
import com.etheller.warsmash.viewer5.PathSolver;
|
||||
import com.etheller.warsmash.viewer5.RenderBatch;
|
||||
import com.etheller.warsmash.viewer5.Scene;
|
||||
import com.etheller.warsmash.viewer5.Texture;
|
||||
import com.etheller.warsmash.viewer5.TextureMapper;
|
||||
|
||||
@ -32,6 +33,10 @@ public class MdxSimpleInstance extends BatchedInstance {
|
||||
public void renderTranslucent() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateLights(final Scene scene2) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
}
|
||||
|
@ -0,0 +1,20 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.mdx;
|
||||
|
||||
import com.etheller.warsmash.datasources.DataSource;
|
||||
import com.etheller.warsmash.viewer5.CanvasProvider;
|
||||
import com.etheller.warsmash.viewer5.ModelViewer;
|
||||
import com.etheller.warsmash.viewer5.SceneLightManager;
|
||||
|
||||
public class MdxViewer extends ModelViewer {
|
||||
|
||||
public MdxViewer(final DataSource dataSource, final CanvasProvider canvas) {
|
||||
super(dataSource, canvas);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SceneLightManager createLightManager() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -155,9 +155,10 @@ public class SetupGeosets {
|
||||
}
|
||||
}
|
||||
|
||||
final boolean unselectable = geoset.getSelectionFlags() == 4;
|
||||
final Geoset vGeoset = new Geoset(model, model.getGeosets().size(), positionOffset, normalOffset,
|
||||
uvOffset, skinOffset, faceOffset, vertices, faces.length, openGLSkinType, skinStride,
|
||||
boneCountOffsetBytes);
|
||||
boneCountOffsetBytes, unselectable, geoset);
|
||||
|
||||
model.getGeosets().add(vGeoset);
|
||||
|
||||
|
@ -67,6 +67,7 @@ public class SequenceUtils {
|
||||
filtered.sort(STAND_SEQUENCE_COMPARATOR);
|
||||
|
||||
int i = 0;
|
||||
final double randomRoll = Math.random() * 100;
|
||||
for (final int l = filtered.size(); i < l; i++) {
|
||||
final Sequence sequence = filtered.get(i).sequence;
|
||||
final float rarity = sequence.getRarity();
|
||||
@ -75,7 +76,7 @@ public class SequenceUtils {
|
||||
break;
|
||||
}
|
||||
|
||||
if ((Math.random() * 10) > rarity) {
|
||||
if (randomRoll < (10 - rarity)) {
|
||||
return filtered.get(i);
|
||||
}
|
||||
}
|
||||
@ -98,6 +99,7 @@ public class SequenceUtils {
|
||||
filtered.sort(STAND_SEQUENCE_COMPARATOR);
|
||||
|
||||
int i = 0;
|
||||
final double randomRoll = Math.random() * 100;
|
||||
for (final int l = filtered.size(); i < l; i++) {
|
||||
final Sequence sequence = filtered.get(i).sequence;
|
||||
final float rarity = sequence.getRarity();
|
||||
@ -106,7 +108,7 @@ public class SequenceUtils {
|
||||
break;
|
||||
}
|
||||
|
||||
if (((Math.random() * 10) > rarity) && allowRarityVariations) {
|
||||
if ((randomRoll < (10 - rarity)) && allowRarityVariations) {
|
||||
return filtered.get(i);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x;
|
||||
|
||||
public class W3xSceneLight {
|
||||
|
||||
public static enum Type {
|
||||
OMNIDIRECTIONAL,
|
||||
DIRECTIONAL;
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.etheller.warsmash.viewer5.SceneLightInstance;
|
||||
import com.etheller.warsmash.viewer5.SceneLightManager;
|
||||
import com.etheller.warsmash.viewer5.gl.DataTexture;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.LightInstance;
|
||||
|
||||
public class W3xSceneLightManager implements SceneLightManager {
|
||||
public final List<LightInstance> lights;
|
||||
private final DataTexture unitLightsTexture;
|
||||
private final DataTexture terrainLightsTexture;
|
||||
|
||||
public W3xSceneLightManager(final War3MapViewer viewer) {
|
||||
this.lights = new ArrayList<>();
|
||||
this.unitLightsTexture = new DataTexture(viewer.gl, 4, 4, 1);
|
||||
this.terrainLightsTexture = new DataTexture(viewer.gl, 4, 4, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(final SceneLightInstance lightInstance) {
|
||||
// TODO redesign to avoid cast
|
||||
final LightInstance mdxLight = (LightInstance) lightInstance;
|
||||
this.lights.add(mdxLight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(final SceneLightInstance lightInstance) {
|
||||
// TODO redesign to avoid cast
|
||||
final LightInstance mdxLight = (LightInstance) lightInstance;
|
||||
this.lights.remove(mdxLight);
|
||||
}
|
||||
}
|
@ -42,10 +42,12 @@ import com.etheller.warsmash.parsers.w3x.w3i.War3MapW3i;
|
||||
import com.etheller.warsmash.parsers.w3x.wpm.War3MapWpm;
|
||||
import com.etheller.warsmash.units.DataTable;
|
||||
import com.etheller.warsmash.units.Element;
|
||||
import com.etheller.warsmash.units.StandardObjectData;
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData;
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject;
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData.WorldEditorDataType;
|
||||
import com.etheller.warsmash.util.MappedData;
|
||||
import com.etheller.warsmash.util.RenderMathUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.etheller.warsmash.util.WarsmashConstants;
|
||||
import com.etheller.warsmash.util.WorldEditStrings;
|
||||
@ -56,6 +58,7 @@ import com.etheller.warsmash.viewer5.ModelInstance;
|
||||
import com.etheller.warsmash.viewer5.ModelViewer;
|
||||
import com.etheller.warsmash.viewer5.PathSolver;
|
||||
import com.etheller.warsmash.viewer5.Scene;
|
||||
import com.etheller.warsmash.viewer5.SceneLightManager;
|
||||
import com.etheller.warsmash.viewer5.Texture;
|
||||
import com.etheller.warsmash.viewer5.WorldScene;
|
||||
import com.etheller.warsmash.viewer5.gl.WebGL;
|
||||
@ -118,7 +121,6 @@ public class War3MapViewer extends ModelViewer {
|
||||
public SolverParams solverParams = new SolverParams();
|
||||
public WorldScene worldScene;
|
||||
public boolean anyReady;
|
||||
public boolean terrainCliffsAndWaterLoaded;
|
||||
public MappedData terrainData = new MappedData();
|
||||
public MappedData cliffTypesData = new MappedData();
|
||||
public MappedData waterData = new MappedData();
|
||||
@ -151,9 +153,11 @@ public class War3MapViewer extends ModelViewer {
|
||||
public List<RenderUnit> selected = new ArrayList<>();
|
||||
private DataTable unitAckSoundsTable;
|
||||
private DataTable unitCombatSoundsTable;
|
||||
private DataTable miscData;
|
||||
public DataTable miscData;
|
||||
private DataTable unitGlobalStrings;
|
||||
private MdxComplexInstance confirmationInstance;
|
||||
private MdxComplexInstance dncUnit;
|
||||
private MdxComplexInstance dncTerrain;
|
||||
public CSimulation simulation;
|
||||
private float updateTime;
|
||||
|
||||
@ -195,7 +199,6 @@ public class War3MapViewer extends ModelViewer {
|
||||
stringDataCallback);
|
||||
|
||||
// == when loaded, which is always in our system ==
|
||||
this.terrainCliffsAndWaterLoaded = true;
|
||||
this.terrainData.load(terrain.data.toString());
|
||||
this.cliffTypesData.load(cliffTypes.data.toString());
|
||||
this.waterData.load(water.data.toString());
|
||||
@ -254,6 +257,9 @@ public class War3MapViewer extends ModelViewer {
|
||||
try (InputStream miscDataTxtStream = this.dataSource.getResourceAsStream("Units\\MiscGame.txt")) {
|
||||
this.miscData.readTXT(miscDataTxtStream, true);
|
||||
}
|
||||
try (InputStream miscDataTxtStream = this.dataSource.getResourceAsStream("UI\\SoundInfo\\MiscData.txt")) {
|
||||
this.miscData.readTXT(miscDataTxtStream, true);
|
||||
}
|
||||
this.unitGlobalStrings = new DataTable(worldEditStrings);
|
||||
try (InputStream miscDataTxtStream = this.dataSource.getResourceAsStream("Units\\UnitGlobalStrings.txt")) {
|
||||
this.unitGlobalStrings.readTXT(miscDataTxtStream, true);
|
||||
@ -342,8 +348,11 @@ public class War3MapViewer extends ModelViewer {
|
||||
|
||||
final War3MapWpm terrainPathing = this.mapMpq.readPathing();
|
||||
|
||||
final StandardObjectData standardObjectData = new StandardObjectData(this.dataSource);
|
||||
this.worldEditData = standardObjectData.getWorldEditData();
|
||||
|
||||
this.terrain = new Terrain(terrainData, terrainPathing, w3iFile, this.webGL, this.dataSource, worldEditStrings,
|
||||
this);
|
||||
this, this.worldEditData);
|
||||
|
||||
final float[] centerOffset = terrainData.getCenterOffset();
|
||||
final int[] mapSize = terrainData.getMapSize();
|
||||
@ -363,13 +372,6 @@ public class War3MapViewer extends ModelViewer {
|
||||
this.confirmationInstance.setSequence(0);
|
||||
this.confirmationInstance.setScene(this.worldScene);
|
||||
|
||||
if (this.terrainCliffsAndWaterLoaded) {
|
||||
this.loadTerrainCliffsAndWater(terrainData);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("transcription of JS has not loaded a map and has no JS async promises");
|
||||
}
|
||||
|
||||
final Warcraft3MapObjectData modifications = this.mapMpq.readModifications();
|
||||
this.simulation = new CSimulation(this.miscData, modifications.getUnits(), modifications.getAbilities(),
|
||||
new SimulationRenderController() {
|
||||
@ -388,12 +390,7 @@ public class War3MapViewer extends ModelViewer {
|
||||
final float projectileLaunchY = simulation.getUnitData().getProjectileLaunchY(typeId);
|
||||
final float projectileLaunchZ = simulation.getUnitData().getProjectileLaunchZ(typeId);
|
||||
|
||||
if (missileArt.toLowerCase().endsWith(".mdl")) {
|
||||
missileArt = missileArt.substring(0, missileArt.length() - 4);
|
||||
}
|
||||
if (!missileArt.toLowerCase().endsWith(".mdx")) {
|
||||
missileArt += ".mdx";
|
||||
}
|
||||
missileArt = mdx(missileArt);
|
||||
final float facing = launchFacing;
|
||||
final float sinFacing = (float) Math.sin(facing);
|
||||
final float cosFacing = (float) Math.cos(facing);
|
||||
@ -435,12 +432,7 @@ public class War3MapViewer extends ModelViewer {
|
||||
.getProjectileLaunchX(typeId);
|
||||
final float projectileLaunchY = War3MapViewer.this.simulation.getUnitData()
|
||||
.getProjectileLaunchY(typeId);
|
||||
if (missileArt.toLowerCase().endsWith(".mdl")) {
|
||||
missileArt = missileArt.substring(0, missileArt.length() - 4);
|
||||
}
|
||||
if (!missileArt.toLowerCase().endsWith(".mdx")) {
|
||||
missileArt += ".mdx";
|
||||
}
|
||||
missileArt = mdx(missileArt);
|
||||
final float facing = (float) Math.toRadians(source.getFacing());
|
||||
final float sinFacing = (float) Math.sin(facing);
|
||||
final float cosFacing = (float) Math.cos(facing);
|
||||
@ -507,9 +499,17 @@ public class War3MapViewer extends ModelViewer {
|
||||
|
||||
this.terrain.initShadows();
|
||||
this.terrain.createWaves();
|
||||
|
||||
loadLightsAndShading(tileset);
|
||||
}
|
||||
|
||||
private void loadTerrainCliffsAndWater(final War3MapW3e w3e) {
|
||||
private void loadLightsAndShading(final char tileset) {
|
||||
// TODO this should be set by the war3map.j actually, not by the tileset, so the
|
||||
// call to set day night models is just for testing to make the test look pretty
|
||||
final Element defaultTerrainLights = this.worldEditData.get("TerrainLights");
|
||||
final Element defaultUnitLights = this.worldEditData.get("UnitLights");
|
||||
setDayNightModels(defaultTerrainLights.getField(Character.toString(tileset)),
|
||||
defaultUnitLights.getField(Character.toString(tileset)));
|
||||
|
||||
}
|
||||
|
||||
@ -902,6 +902,10 @@ public class War3MapViewer extends ModelViewer {
|
||||
this.updateTime -= WarsmashConstants.SIMULATION_STEP_TIME;
|
||||
this.simulation.update();
|
||||
}
|
||||
this.dncTerrain.setFrameByRatio(
|
||||
this.simulation.getGameTimeOfDay() / this.simulation.getGameplayConstants().getGameDayHours());
|
||||
this.dncUnit.setFrameByRatio(
|
||||
this.simulation.getGameTimeOfDay() / this.simulation.getGameplayConstants().getGameDayHours());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1007,7 +1011,7 @@ public class War3MapViewer extends ModelViewer {
|
||||
this.worldScene.camera.screenToWorldRay(ray, mousePosHeap);
|
||||
gdxRayHeap.set(ray[0], ray[1], ray[2], ray[3] - ray[0], ray[4] - ray[1], ray[5] - ray[2]);
|
||||
gdxRayHeap.direction.nor();// needed for libgdx
|
||||
Terrain.intersectRayTriangles(gdxRayHeap, this.terrain.softwareGroundMesh.vertices,
|
||||
RenderMathUtils.intersectRayTriangles(gdxRayHeap, this.terrain.softwareGroundMesh.vertices,
|
||||
this.terrain.softwareGroundMesh.indices, 3, out);
|
||||
}
|
||||
|
||||
@ -1144,6 +1148,7 @@ public class War3MapViewer extends ModelViewer {
|
||||
|
||||
private static final int MAXIMUM_ACCEPTED = 1 << 30;
|
||||
private float selectionCircleScaleFactor;
|
||||
private DataTable worldEditData;
|
||||
|
||||
/**
|
||||
* Returns a power of two size for the given target capacity.
|
||||
@ -1240,4 +1245,30 @@ public class War3MapViewer extends ModelViewer {
|
||||
this.textureDotted = textureDotted;
|
||||
}
|
||||
}
|
||||
|
||||
public void setDayNightModels(final String terrainDNCFile, final String unitDNCFile) {
|
||||
final MdxModel terrainDNCModel = (MdxModel) load(mdx(terrainDNCFile), PathSolver.DEFAULT, null);
|
||||
this.dncTerrain = (MdxComplexInstance) terrainDNCModel.addInstance();
|
||||
this.dncTerrain.setSequenceLoopMode(SequenceLoopMode.ALWAYS_LOOP);
|
||||
this.dncTerrain.setSequence(0);
|
||||
final MdxModel unitDNCModel = (MdxModel) load(mdx(unitDNCFile), PathSolver.DEFAULT, null);
|
||||
this.dncUnit = (MdxComplexInstance) unitDNCModel.addInstance();
|
||||
this.dncUnit.setSequenceLoopMode(SequenceLoopMode.ALWAYS_LOOP);
|
||||
this.dncUnit.setSequence(0);
|
||||
}
|
||||
|
||||
private static String mdx(String mdxPath) {
|
||||
if (mdxPath.toLowerCase().endsWith(".mdl")) {
|
||||
mdxPath = mdxPath.substring(0, mdxPath.length() - 4);
|
||||
}
|
||||
if (!mdxPath.toLowerCase().endsWith(".mdx")) {
|
||||
mdxPath += ".mdx";
|
||||
}
|
||||
return mdxPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SceneLightManager createLightManager() {
|
||||
return new W3xSceneLightManager(this);
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,6 @@ import com.badlogic.gdx.math.Intersector;
|
||||
import com.badlogic.gdx.math.Matrix4;
|
||||
import com.badlogic.gdx.math.Rectangle;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import com.badlogic.gdx.math.collision.Ray;
|
||||
import com.etheller.warsmash.datasources.DataSource;
|
||||
import com.etheller.warsmash.parsers.w3x.w3e.Corner;
|
||||
import com.etheller.warsmash.parsers.w3x.w3e.War3MapW3e;
|
||||
@ -34,7 +33,6 @@ import com.etheller.warsmash.parsers.w3x.w3i.War3MapW3i;
|
||||
import com.etheller.warsmash.parsers.w3x.wpm.War3MapWpm;
|
||||
import com.etheller.warsmash.units.DataTable;
|
||||
import com.etheller.warsmash.units.Element;
|
||||
import com.etheller.warsmash.units.StandardObjectData;
|
||||
import com.etheller.warsmash.util.ImageUtils;
|
||||
import com.etheller.warsmash.util.RenderMathUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
@ -127,7 +125,7 @@ public class Terrain {
|
||||
|
||||
public Terrain(final War3MapW3e w3eFile, final War3MapWpm terrainPathing, final War3MapW3i w3iFile,
|
||||
final WebGL webGL, final DataSource dataSource, final WorldEditStrings worldEditStrings,
|
||||
final War3MapViewer viewer) throws IOException {
|
||||
final War3MapViewer viewer, final DataTable worldEditData) throws IOException {
|
||||
this.webGL = webGL;
|
||||
this.viewer = viewer;
|
||||
this.camera = viewer.worldScene.camera;
|
||||
@ -221,8 +219,6 @@ public class Terrain {
|
||||
this.groundTextureToId.put(groundTile.asStringValue(), this.groundTextures.size() - 1);
|
||||
}
|
||||
|
||||
final StandardObjectData standardObjectData = new StandardObjectData(dataSource);
|
||||
final DataTable worldEditData = standardObjectData.getWorldEditData();
|
||||
final Element tilesets = worldEditData.get("TileSets");
|
||||
|
||||
this.blightTextureIndex = this.groundTextures.size();
|
||||
@ -1179,67 +1175,10 @@ public class Terrain {
|
||||
// return out;
|
||||
// }
|
||||
|
||||
static Vector3 best = new Vector3();
|
||||
static Vector3 tmp = new Vector3();
|
||||
static Vector3 tmp1 = new Vector3();
|
||||
static Vector3 tmp2 = new Vector3();
|
||||
static Vector3 tmp3 = new Vector3();
|
||||
private final WaveBuilder waveBuilder;
|
||||
public PathingGrid pathingGrid;
|
||||
private final Rectangle shaderMapBoundsRectangle;
|
||||
|
||||
/**
|
||||
* Intersects the given ray with list of triangles. Returns the nearest
|
||||
* intersection point in intersection
|
||||
*
|
||||
* @param ray The ray
|
||||
* @param vertices the vertices
|
||||
* @param indices the indices, each successive 3 shorts index the 3
|
||||
* vertices of a triangle
|
||||
* @param vertexSize the size of a vertex in floats
|
||||
* @param intersection The nearest intersection point (optional)
|
||||
* @return Whether the ray and the triangles intersect.
|
||||
*/
|
||||
public static boolean intersectRayTriangles(final Ray ray, final float[] vertices, final int[] indices,
|
||||
final int vertexSize, final Vector3 intersection) {
|
||||
float min_dist = Float.MAX_VALUE;
|
||||
boolean hit = false;
|
||||
|
||||
if ((indices.length % 3) != 0) {
|
||||
throw new RuntimeException("triangle list size is not a multiple of 3");
|
||||
}
|
||||
|
||||
for (int i = 0; i < indices.length; i += 3) {
|
||||
final int i1 = indices[i] * vertexSize;
|
||||
final int i2 = indices[i + 1] * vertexSize;
|
||||
final int i3 = indices[i + 2] * vertexSize;
|
||||
|
||||
final boolean result = Intersector.intersectRayTriangle(ray,
|
||||
tmp1.set(vertices[i1], vertices[i1 + 1], vertices[i1 + 2]),
|
||||
tmp2.set(vertices[i2], vertices[i2 + 1], vertices[i2 + 2]),
|
||||
tmp3.set(vertices[i3], vertices[i3 + 1], vertices[i3 + 2]), tmp);
|
||||
|
||||
if (result == true) {
|
||||
final float dist = ray.origin.dst2(tmp);
|
||||
if (dist < min_dist) {
|
||||
min_dist = dist;
|
||||
best.set(tmp);
|
||||
hit = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hit == false) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
if (intersection != null) {
|
||||
intersection.set(best);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class UnloadedTexture {
|
||||
private final int width;
|
||||
private final int height;
|
||||
|
@ -19,6 +19,10 @@ public class CGameplayConstants {
|
||||
private final float boneDecayTime;
|
||||
private final float bulletDeathTime;
|
||||
private final float closeEnoughRange;
|
||||
private final float dawnTimeGameHours;
|
||||
private final float duskTimeGameHours;
|
||||
private final float gameDayHours;
|
||||
private final float gameDayLength;
|
||||
|
||||
public CGameplayConstants(final DataTable parsedDataTable) {
|
||||
final Element miscData = parsedDataTable.get("Misc");
|
||||
@ -30,6 +34,11 @@ public class CGameplayConstants {
|
||||
this.bulletDeathTime = miscData.getFieldFloatValue("BulletDeathTime");
|
||||
this.closeEnoughRange = miscData.getFieldFloatValue("CloseEnoughRange");
|
||||
|
||||
this.dawnTimeGameHours = miscData.getFieldFloatValue("Dawn");
|
||||
this.duskTimeGameHours = miscData.getFieldFloatValue("Dusk");
|
||||
this.gameDayHours = miscData.getFieldFloatValue("DayHours");
|
||||
this.gameDayLength = miscData.getFieldFloatValue("DayLength");
|
||||
|
||||
final CDefenseType[] defenseTypeOrder = { CDefenseType.SMALL, CDefenseType.MEDIUM, CDefenseType.LARGE,
|
||||
CDefenseType.FORT, CDefenseType.NORMAL, CDefenseType.HERO, CDefenseType.DIVINE, CDefenseType.NONE, };
|
||||
this.damageBonusTable = new float[CAttackType.values().length][defenseTypeOrder.length];
|
||||
@ -80,4 +89,20 @@ public class CGameplayConstants {
|
||||
public float getCloseEnoughRange() {
|
||||
return this.closeEnoughRange;
|
||||
}
|
||||
|
||||
public float getGameDayHours() {
|
||||
return this.gameDayHours;
|
||||
}
|
||||
|
||||
public float getGameDayLength() {
|
||||
return this.gameDayLength;
|
||||
}
|
||||
|
||||
public float getDawnTimeGameHours() {
|
||||
return this.dawnTimeGameHours;
|
||||
}
|
||||
|
||||
public float getDuskTimeGameHours() {
|
||||
return this.duskTimeGameHours;
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ public class CSimulation {
|
||||
private final CPathfindingProcessor pathfindingProcessor;
|
||||
private final CGameplayConstants gameplayConstants;
|
||||
private final Random seededRandom;
|
||||
private float currentGameDayTimeElapsed;
|
||||
|
||||
public CSimulation(final DataTable miscData, final MutableObjectData parsedUnitData,
|
||||
final MutableObjectData parsedAbilityData, final SimulationRenderController simulationRenderController,
|
||||
@ -145,6 +146,13 @@ public class CSimulation {
|
||||
this.projectiles.addAll(this.newProjectiles);
|
||||
this.newProjectiles.clear();
|
||||
this.gameTurnTick++;
|
||||
this.currentGameDayTimeElapsed = (this.currentGameDayTimeElapsed + WarsmashConstants.SIMULATION_STEP_TIME)
|
||||
% this.gameplayConstants.getGameDayLength();
|
||||
}
|
||||
|
||||
public float getGameTimeOfDay() {
|
||||
return (this.currentGameDayTimeElapsed / this.gameplayConstants.getGameDayLength())
|
||||
* this.gameplayConstants.getGameDayHours();
|
||||
}
|
||||
|
||||
public int getGameTurnTick() {
|
||||
|
@ -132,6 +132,26 @@ public class CUnitData {
|
||||
final int speed = unitType.getFieldAsInteger(MOVEMENT_SPEED_BASE, 0);
|
||||
final int defense = unitType.getFieldAsInteger(DEFENSE, 0);
|
||||
|
||||
final CUnitType unitTypeInstance = getUnitTypeInstance(typeId, buildingPathingPixelMap, unitType);
|
||||
|
||||
final CUnit unit = new CUnit(handleId, playerIndex, x, y, life, typeId, facing, manaInitial, life, manaMaximum,
|
||||
speed, defense, unitTypeInstance);
|
||||
if (speed > 0) {
|
||||
unit.add(simulation, CAbilityMove.INSTANCE);
|
||||
unit.add(simulation, CAbilityPatrol.INSTANCE);
|
||||
unit.add(simulation, CAbilityHoldPosition.INSTANCE);
|
||||
unit.add(simulation, CAbilityStop.INSTANCE);
|
||||
}
|
||||
final int dmgDice1 = unitType.getFieldAsInteger(ATTACK1_DMG_DICE, 0);
|
||||
final int dmgDice2 = unitType.getFieldAsInteger(ATTACK2_DMG_DICE, 0);
|
||||
if ((dmgDice1 != 0) || (dmgDice2 != 0)) {
|
||||
unit.add(simulation, CAbilityAttack.INSTANCE);
|
||||
}
|
||||
return unit;
|
||||
}
|
||||
|
||||
private CUnitType getUnitTypeInstance(final War3ID typeId, final BufferedImage buildingPathingPixelMap,
|
||||
final MutableGameObject unitType) {
|
||||
CUnitType unitTypeInstance = this.unitIdToUnitType.get(typeId);
|
||||
if (unitTypeInstance == null) {
|
||||
final float moveHeight = unitType.getFieldAsFloat(MOVE_HEIGHT, 0);
|
||||
@ -252,21 +272,7 @@ public class CUnitData {
|
||||
targetedAs);
|
||||
this.unitIdToUnitType.put(typeId, unitTypeInstance);
|
||||
}
|
||||
|
||||
final CUnit unit = new CUnit(handleId, playerIndex, x, y, life, typeId, facing, manaInitial, life, manaMaximum,
|
||||
speed, defense, unitTypeInstance);
|
||||
if (speed > 0) {
|
||||
unit.add(simulation, CAbilityMove.INSTANCE);
|
||||
unit.add(simulation, CAbilityPatrol.INSTANCE);
|
||||
unit.add(simulation, CAbilityHoldPosition.INSTANCE);
|
||||
unit.add(simulation, CAbilityStop.INSTANCE);
|
||||
}
|
||||
final int dmgDice1 = unitType.getFieldAsInteger(ATTACK1_DMG_DICE, 0);
|
||||
final int dmgDice2 = unitType.getFieldAsInteger(ATTACK2_DMG_DICE, 0);
|
||||
if ((dmgDice1 != 0) || (dmgDice2 != 0)) {
|
||||
unit.add(simulation, CAbilityAttack.INSTANCE);
|
||||
}
|
||||
return unit;
|
||||
return unitTypeInstance;
|
||||
}
|
||||
|
||||
private CUnitAttack createAttack(final float animationBackswingPoint, final float animationDamagePoint,
|
||||
|
@ -18,6 +18,7 @@ import com.etheller.warsmash.parsers.fdf.GameUI;
|
||||
import com.etheller.warsmash.parsers.fdf.datamodel.AnchorDefinition;
|
||||
import com.etheller.warsmash.parsers.fdf.datamodel.FramePoint;
|
||||
import com.etheller.warsmash.parsers.fdf.frames.SetPoint;
|
||||
import com.etheller.warsmash.parsers.fdf.frames.SpriteFrame;
|
||||
import com.etheller.warsmash.parsers.fdf.frames.StringFrame;
|
||||
import com.etheller.warsmash.parsers.fdf.frames.TextureFrame;
|
||||
import com.etheller.warsmash.parsers.fdf.frames.UIFrame;
|
||||
@ -55,7 +56,7 @@ public class MeleeUI implements CUnitStateListener {
|
||||
private StringFrame resourceBarLumberText;
|
||||
private StringFrame resourceBarSupplyText;
|
||||
private StringFrame resourceBarUpkeepText;
|
||||
private UIFrame timeIndicator;
|
||||
private SpriteFrame timeIndicator;
|
||||
private UIFrame unitPortrait;
|
||||
private StringFrame unitLifeText;
|
||||
private StringFrame unitManaText;
|
||||
@ -107,7 +108,7 @@ public class MeleeUI implements CUnitStateListener {
|
||||
// =================================
|
||||
// Load skins and templates
|
||||
// =================================
|
||||
this.rootFrame = new GameUI(this.dataSource, GameUI.loadSkin(this.dataSource, 3), this.uiViewport,
|
||||
this.rootFrame = new GameUI(this.dataSource, GameUI.loadSkin(this.dataSource, 0), this.uiViewport,
|
||||
this.fontGenerator, this.uiScene, this.war3MapViewer);
|
||||
try {
|
||||
this.rootFrame.loadTOCFile("UI\\FrameDef\\FrameDef.toc");
|
||||
@ -148,7 +149,9 @@ public class MeleeUI implements CUnitStateListener {
|
||||
this.resourceBarUpkeepText.setColor(Color.RED);
|
||||
|
||||
// Create the Time Indicator (clock)
|
||||
this.timeIndicator = this.rootFrame.createFrame("TimeOfDayIndicator", this.rootFrame, 0, 0);
|
||||
this.timeIndicator = (SpriteFrame) this.rootFrame.createFrame("TimeOfDayIndicator", this.rootFrame, 0, 0);
|
||||
this.timeIndicator.setSequence(0); // play the stand
|
||||
this.timeIndicator.setAnimationSpeed(0.0f); // do not advance automatically
|
||||
|
||||
// Create the unit portrait stuff
|
||||
this.portrait = new Portrait(this.war3MapViewer);
|
||||
@ -221,6 +224,9 @@ public class MeleeUI implements CUnitStateListener {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.timeIndicator.setFrameByRatio(this.war3MapViewer.simulation.getGameTimeOfDay()
|
||||
/ this.war3MapViewer.simulation.getGameplayConstants().getGameDayHours());
|
||||
}
|
||||
|
||||
public void portraitTalk() {
|
||||
@ -229,12 +235,12 @@ public class MeleeUI implements CUnitStateListener {
|
||||
|
||||
private static final class Portrait {
|
||||
private MdxComplexInstance modelInstance;
|
||||
private final WarsmashGdxMapGame.CameraManager portraitCameraManager;
|
||||
private final WarsmashGdxMapGame.PortraitCameraManager portraitCameraManager;
|
||||
private final Scene portraitScene;
|
||||
|
||||
public Portrait(final War3MapViewer war3MapViewer) {
|
||||
this.portraitScene = war3MapViewer.addSimpleScene();
|
||||
this.portraitCameraManager = new WarsmashGdxMapGame.CameraManager();
|
||||
this.portraitCameraManager = new WarsmashGdxMapGame.PortraitCameraManager();
|
||||
this.portraitCameraManager.setupCamera(this.portraitScene);
|
||||
this.portraitScene.camera.viewport(new Rectangle(100, 0, 6400, 48));
|
||||
}
|
||||
@ -303,6 +309,11 @@ public class MeleeUI implements CUnitStateListener {
|
||||
this.simpleNameValue.setText(unit.getSimulationUnit().getUnitType().getName());
|
||||
String classText = null;
|
||||
for (final CUnitClassification classification : unit.getSimulationUnit().getClassifications()) {
|
||||
if ((classification == CUnitClassification.MECHANICAL)
|
||||
&& unit.getSimulationUnit().getUnitType().isBuilding()) {
|
||||
// buildings dont display MECHANICAL
|
||||
continue;
|
||||
}
|
||||
if (classification.getDisplayName() != null) {
|
||||
classText = classification.getDisplayName();
|
||||
}
|
||||
@ -325,7 +336,11 @@ public class MeleeUI implements CUnitStateListener {
|
||||
}
|
||||
this.simpleBuildingActionLabel.setText("");
|
||||
|
||||
if (unit.getSimulationUnit().getUnitType().getAttacks().size() > 0) {
|
||||
final boolean anyAttacks = unit.getSimulationUnit().getUnitType().getAttacks().size() > 0;
|
||||
final UIFrame localArmorIcon;
|
||||
final TextureFrame localArmorIconBackdrop;
|
||||
final StringFrame localArmorInfoPanelIconValue;
|
||||
if (anyAttacks) {
|
||||
final CUnitAttack attackOne = unit.getSimulationUnit().getUnitType().getAttacks().get(0);
|
||||
this.attack1Icon.setVisible(attackOne.isShowUI());
|
||||
this.attack1IconBackdrop.setTexture(this.damageBackdrops.getTexture(attackOne.getAttackType()));
|
||||
@ -339,21 +354,29 @@ public class MeleeUI implements CUnitStateListener {
|
||||
else {
|
||||
this.attack2Icon.setVisible(false);
|
||||
}
|
||||
|
||||
localArmorIcon = this.armorIcon;
|
||||
localArmorIconBackdrop = this.armorIconBackdrop;
|
||||
localArmorInfoPanelIconValue = this.armorInfoPanelIconValue;
|
||||
}
|
||||
else {
|
||||
this.attack1Icon.setVisible(false);
|
||||
this.armorIcon.setVisible(false);
|
||||
this.attack2Icon.setVisible(false);
|
||||
|
||||
localArmorIcon = this.attack1Icon;
|
||||
localArmorIconBackdrop = this.attack1IconBackdrop;
|
||||
localArmorInfoPanelIconValue = this.attack1InfoPanelIconValue;
|
||||
}
|
||||
|
||||
this.armorIcon.setVisible(true);
|
||||
localArmorIcon.setVisible(true);
|
||||
final Texture defenseTexture = this.defenseBackdrops
|
||||
.getTexture(unit.getSimulationUnit().getUnitType().getDefenseType());
|
||||
if (defenseTexture == null) {
|
||||
throw new RuntimeException(
|
||||
unit.getSimulationUnit().getUnitType().getDefenseType() + " can't find texture!");
|
||||
}
|
||||
this.armorIconBackdrop.setTexture(defenseTexture);
|
||||
this.armorInfoPanelIconValue.setText(Integer.toString(unit.getSimulationUnit().getDefense()));
|
||||
localArmorIconBackdrop.setTexture(defenseTexture);
|
||||
localArmorInfoPanelIconValue.setText(Integer.toString(unit.getSimulationUnit().getDefense()));
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user