mirror of
https://github.com/Retera/WarsmashModEngine.git
synced 2022-07-31 17:38:59 +02:00
Clickable destructables, some cliff changes, shadows on bridges
This commit is contained in:
parent
9da2160565
commit
4f7368eb57
@ -1,5 +1,5 @@
|
||||
[DataSources]
|
||||
Count=7
|
||||
Count=8
|
||||
Type00=MPQ
|
||||
Path00="D:\Games\Warcraft III Patch 1.22\war3.mpq"
|
||||
Type01=MPQ
|
||||
@ -13,12 +13,15 @@ Path04="..\..\resources"
|
||||
Type05=Folder
|
||||
Path05="D:\Backups\Warsmash\Data"
|
||||
Type06=Folder
|
||||
Path06="."
|
||||
Path06="D:\Games\Warcraft III Patch 1.22\Maps"
|
||||
Type07=Folder
|
||||
Path07="."
|
||||
|
||||
[Map]
|
||||
//FilePath="CombatUnitTests.w3x"
|
||||
//FilePath="PitchRoll.w3x"
|
||||
FilePath="PeonStartingBase.w3x"
|
||||
//FilePath="PeonStartingBase.w3x"
|
||||
FilePath="MyStromguarde.w3m"
|
||||
//FilePath="ColdArrows.w3m"
|
||||
//FilePath="DungeonGoldMine.w3m"
|
||||
//FilePath="PlayerPeasants.w3m"
|
||||
|
@ -56,7 +56,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.ui.command.SettableCommandErro
|
||||
|
||||
public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProvider, InputProcessor {
|
||||
private static final boolean ENABLE_AUDIO = true;
|
||||
private static final boolean ENABLE_MUSIC = false;
|
||||
private static final boolean ENABLE_MUSIC = true;
|
||||
private DataSource codebase;
|
||||
private War3MapViewer viewer;
|
||||
private final Rectangle tempRect = new Rectangle();
|
||||
@ -215,7 +215,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
||||
final String musicField = rootFrame.getSkinField("Music_V1");
|
||||
final String[] musics = musicField.split(";");
|
||||
String musicPath = musics[(int) (Math.random() * musics.length)];
|
||||
if (true) {
|
||||
if (false) {
|
||||
musicPath = "Sound\\Music\\mp3Music\\OrcTheme.mp3";
|
||||
}
|
||||
final Music music = Gdx.audio.newMusic(
|
||||
|
82
core/src/com/etheller/warsmash/util/FixedIntersector.java
Normal file
82
core/src/com/etheller/warsmash/util/FixedIntersector.java
Normal file
@ -0,0 +1,82 @@
|
||||
package com.etheller.warsmash.util;
|
||||
|
||||
import com.badlogic.gdx.math.Intersector;
|
||||
import com.badlogic.gdx.math.MathUtils;
|
||||
import com.badlogic.gdx.math.Plane;
|
||||
import com.badlogic.gdx.math.Plane.PlaneSide;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import com.badlogic.gdx.math.collision.Ray;
|
||||
|
||||
public class FixedIntersector {
|
||||
|
||||
private final static Vector3 v0 = new Vector3();
|
||||
private final static Vector3 v1 = new Vector3();
|
||||
private final static Vector3 v2 = new Vector3();
|
||||
|
||||
private static final Plane p = new Plane(new Vector3(), 0);
|
||||
private static final Vector3 i = new Vector3();
|
||||
|
||||
/**
|
||||
* Intersect a {@link Ray} and a triangle, returning the intersection point in
|
||||
* intersection.
|
||||
*
|
||||
* @param ray The ray
|
||||
* @param t1 The first vertex of the triangle
|
||||
* @param t2 The second vertex of the triangle
|
||||
* @param t3 The third vertex of the triangle
|
||||
* @param intersection The intersection point (optional)
|
||||
* @return True in case an intersection is present.
|
||||
*/
|
||||
public static boolean intersectRayTriangle(final Ray ray, final Vector3 t1, final Vector3 t2, final Vector3 t3,
|
||||
final Vector3 intersection) {
|
||||
if (t2.epsilonEquals(t3)) {
|
||||
return false;
|
||||
}
|
||||
final Vector3 edge1 = v0.set(t2).sub(t1);
|
||||
final Vector3 edge2 = v1.set(t3).sub(t1);
|
||||
|
||||
final Vector3 pvec = v2.set(ray.direction).crs(edge2);
|
||||
float det = edge1.dot(pvec);
|
||||
if (MathUtils.isZero(det)) {
|
||||
p.set(t1, t2, t3);
|
||||
if ((p.testPoint(ray.origin) == PlaneSide.OnPlane)
|
||||
&& Intersector.isPointInTriangle(ray.origin, t1, t2, t3)) {
|
||||
if (intersection != null) {
|
||||
intersection.set(ray.origin);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
det = 1.0f / det;
|
||||
|
||||
final Vector3 tvec = i.set(ray.origin).sub(t1);
|
||||
final float u = tvec.dot(pvec) * det;
|
||||
if ((u < 0.0f) || (u > 1.0f)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Vector3 qvec = tvec.crs(edge1);
|
||||
final float v = ray.direction.dot(qvec) * det;
|
||||
if ((v < 0.0f) || ((u + v) > 1.0f)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final float t = edge2.dot(qvec) * det;
|
||||
if (t < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (intersection != null) {
|
||||
if (t <= MathUtils.FLOAT_ROUNDING_ERROR) {
|
||||
intersection.set(ray.origin);
|
||||
}
|
||||
else {
|
||||
ray.getEndPoint(intersection, t);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -486,7 +486,7 @@ public enum RenderMathUtils {
|
||||
final int i2 = indices[i + 1] * vertexSize;
|
||||
final int i3 = indices[i + 2] * vertexSize;
|
||||
|
||||
final boolean result = Intersector.intersectRayTriangle(ray,
|
||||
final boolean result = FixedIntersector.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);
|
||||
|
@ -1,42 +0,0 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x;
|
||||
|
||||
import com.badlogic.gdx.math.Rectangle;
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject;
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData.WorldEditorDataType;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.environment.BuildingShadow;
|
||||
|
||||
public class RenderDestructable extends RenderDoodad {
|
||||
private static final War3ID TEX_FILE = War3ID.fromString("btxf");
|
||||
private static final War3ID TEX_ID = War3ID.fromString("btxi");
|
||||
|
||||
private final float life;
|
||||
public Rectangle walkableBounds;
|
||||
|
||||
public RenderDestructable(final War3MapViewer map, final MdxModel model, final MutableGameObject row,
|
||||
final com.etheller.warsmash.parsers.w3x.doo.Doodad doodad, final WorldEditorDataType type,
|
||||
final float maxPitch, final float maxRoll, final float life, final BuildingShadow destructableShadow) {
|
||||
super(map, model, row, doodad, type, maxPitch, maxRoll);
|
||||
this.life = life;
|
||||
String replaceableTextureFile = row.getFieldAsString(TEX_FILE, 0);
|
||||
final int replaceableTextureId = row.getFieldAsInteger(TEX_ID, 0);
|
||||
if ((replaceableTextureFile != null) && (replaceableTextureFile.length() > 1)) {
|
||||
final int dotIndex = replaceableTextureFile.lastIndexOf('.');
|
||||
if (dotIndex != -1) {
|
||||
replaceableTextureFile = replaceableTextureFile.substring(0, dotIndex);
|
||||
}
|
||||
replaceableTextureFile += ".blp";
|
||||
this.instance.setReplaceableTexture(replaceableTextureId, replaceableTextureFile);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrimaryTag getAnimation() {
|
||||
if (this.life <= 0) {
|
||||
return PrimaryTag.DEATH;
|
||||
}
|
||||
return super.getAnimation();
|
||||
}
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x;
|
||||
|
||||
import com.badlogic.gdx.math.Quaternion;
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject;
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData.WorldEditorDataType;
|
||||
import com.etheller.warsmash.util.RenderMathUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.etheller.warsmash.viewer5.ModelInstance;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.SequenceLoopMode;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
|
||||
|
||||
public class RenderDoodad {
|
||||
private static final int SAMPLE_RADIUS = 4;
|
||||
public final ModelInstance instance;
|
||||
private final MutableGameObject row;
|
||||
private final float maxPitch;
|
||||
private final float maxRoll;
|
||||
|
||||
public RenderDoodad(final War3MapViewer map, final MdxModel model, final MutableGameObject row,
|
||||
final com.etheller.warsmash.parsers.w3x.doo.Doodad doodad, final WorldEditorDataType type,
|
||||
final float maxPitch, final float maxRoll) {
|
||||
this.maxPitch = maxPitch;
|
||||
this.maxRoll = maxRoll;
|
||||
final boolean isSimple = row.readSLKTagBoolean("lightweight");
|
||||
ModelInstance instance;
|
||||
|
||||
if (isSimple && false) {
|
||||
instance = model.addInstance(1);
|
||||
} else {
|
||||
instance = model.addInstance();
|
||||
((MdxComplexInstance) instance).setSequenceLoopMode(SequenceLoopMode.NEVER_LOOP);
|
||||
}
|
||||
|
||||
instance.move(doodad.getLocation());
|
||||
// TODO: the following pitch/roll system is a heuristic, and we probably want to
|
||||
// revisit it later.
|
||||
// Specifically, I was pretty convinced that whichever is applied first
|
||||
// (pitch/roll) should be used to do a projection onto the already-tilted plane
|
||||
// to find the angle used for the other of the two
|
||||
// (instead of measuring down from an imaginary flat ground plane, as we do
|
||||
// currently).
|
||||
final float facingRadians = doodad.getAngle();
|
||||
float pitch, roll;
|
||||
final float x = doodad.getLocation()[0];
|
||||
final float y = doodad.getLocation()[1];
|
||||
final float pitchSampleForwardX = x + (SAMPLE_RADIUS * (float) Math.cos(facingRadians));
|
||||
final float pitchSampleForwardY = y + (SAMPLE_RADIUS * (float) Math.sin(facingRadians));
|
||||
final float pitchSampleBackwardX = x - (SAMPLE_RADIUS * (float) Math.cos(facingRadians));
|
||||
final float pitchSampleBackwardY = y - (SAMPLE_RADIUS * (float) Math.sin(facingRadians));
|
||||
final float pitchSampleGroundHeight1 = map.terrain.getGroundHeight(pitchSampleBackwardX, pitchSampleBackwardY);
|
||||
final float pitchSampleGorundHeight2 = map.terrain.getGroundHeight(pitchSampleForwardX, pitchSampleForwardY);
|
||||
pitch = Math.max(-maxPitch, Math.min(maxPitch,
|
||||
(float) Math.atan2(pitchSampleGorundHeight2 - pitchSampleGroundHeight1, SAMPLE_RADIUS * 2)));
|
||||
final double leftOfFacingAngle = facingRadians + (Math.PI / 2);
|
||||
final float rollSampleForwardX = x + (SAMPLE_RADIUS * (float) Math.cos(leftOfFacingAngle));
|
||||
final float rollSampleForwardY = y + (SAMPLE_RADIUS * (float) Math.sin(leftOfFacingAngle));
|
||||
final float rollSampleBackwardX = x - (SAMPLE_RADIUS * (float) Math.cos(leftOfFacingAngle));
|
||||
final float rollSampleBackwardY = y - (SAMPLE_RADIUS * (float) Math.sin(leftOfFacingAngle));
|
||||
final float rollSampleGroundHeight1 = map.terrain.getGroundHeight(rollSampleBackwardX, rollSampleBackwardY);
|
||||
final float rollSampleGroundHeight2 = map.terrain.getGroundHeight(rollSampleForwardX, rollSampleForwardY);
|
||||
roll = Math.max(-maxRoll, Math.min(maxRoll,
|
||||
(float) Math.atan2(rollSampleGroundHeight2 - rollSampleGroundHeight1, SAMPLE_RADIUS * 2)));
|
||||
instance.rotate(new Quaternion().setFromAxisRad(RenderMathUtils.VEC3_UNIT_Z, facingRadians));
|
||||
instance.rotate(new Quaternion().setFromAxisRad(RenderMathUtils.VEC3_UNIT_Y, -pitch));
|
||||
instance.rotate(new Quaternion().setFromAxisRad(RenderMathUtils.VEC3_UNIT_X, roll));
|
||||
// instance.rotate(new Quaternion().setEulerAnglesRad(facingRadians, 0, 0));
|
||||
instance.scale(doodad.getScale());
|
||||
if (type == WorldEditorDataType.DOODADS) {
|
||||
final float defScale = row.readSLKTagFloat("defScale");
|
||||
instance.uniformScale(defScale);
|
||||
}
|
||||
instance.setScene(map.worldScene);
|
||||
|
||||
this.instance = instance;
|
||||
this.row = row;
|
||||
}
|
||||
|
||||
public PrimaryTag getAnimation() {
|
||||
return PrimaryTag.STAND;
|
||||
}
|
||||
}
|
@ -22,6 +22,7 @@ import com.etheller.warsmash.viewer5.ViewerTextureRenderable;
|
||||
*/
|
||||
public class SplatModel {
|
||||
private static final int MAX_VERTICES = 65000;
|
||||
private static final float NO_ABS_HEIGHT = -257f;
|
||||
private final ViewerTextureRenderable texture;
|
||||
private final List<Batch> batches;
|
||||
public final float[] color;
|
||||
@ -77,6 +78,7 @@ public class SplatModel {
|
||||
private void loadBatches(final GL30 gl, final float[] centerOffset) {
|
||||
final List<float[]> vertices = new ArrayList<>();
|
||||
final List<float[]> uvs = new ArrayList<>();
|
||||
final List<float[]> absoluteHeights = new ArrayList<>();
|
||||
final List<int[]> indices = new ArrayList<>();
|
||||
final List<SplatMover> batchRenderUnits = new ArrayList<>();
|
||||
final int instances = this.locations.size();
|
||||
@ -112,9 +114,10 @@ public class SplatModel {
|
||||
|
||||
final int step = (ix1 - ix0) + 1;
|
||||
if ((start + numVertsToCrate) > MAX_VERTICES) {
|
||||
this.addBatch(gl, vertices, uvs, indices, batchRenderUnits);
|
||||
this.addBatch(gl, vertices, uvs, absoluteHeights, indices, batchRenderUnits);
|
||||
vertices.clear();
|
||||
uvs.clear();
|
||||
absoluteHeights.clear();
|
||||
indices.clear();
|
||||
batchRenderUnits.clear();
|
||||
start = 0;
|
||||
@ -130,9 +133,12 @@ public class SplatModel {
|
||||
vertices.add(vertex);
|
||||
final float[] uv = new float[] { (x - x0) / uvXScale, 1.0f - ((y - y0) / uvYScale) };
|
||||
uvs.add(uv);
|
||||
final float[] absHeight = new float[] { NO_ABS_HEIGHT };
|
||||
absoluteHeights.add(absHeight);
|
||||
if (splatMover != null) {
|
||||
splatMover.vertices.add(vertex);
|
||||
splatMover.uvs.add(uv);
|
||||
splatMover.absoluteHeights.add(absHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -152,8 +158,11 @@ public class SplatModel {
|
||||
vertices.add(vertex);
|
||||
final float[] uv = new float[] { (x - x0) / uvXScale, 1.0f - ((y - y0) / uvYScale) };
|
||||
uvs.add(uv);
|
||||
final float[] absHeight = new float[] { NO_ABS_HEIGHT };
|
||||
absoluteHeights.add(absHeight);
|
||||
splatMover.vertices.add(vertex);
|
||||
splatMover.uvs.add(uv);
|
||||
splatMover.absoluteHeights.add(absHeight);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < (iy1 - iy0); ++i) {
|
||||
@ -179,7 +188,7 @@ public class SplatModel {
|
||||
|
||||
}
|
||||
if (indices.size() > 0) {
|
||||
this.addBatch(gl, vertices, uvs, indices, batchRenderUnits);
|
||||
this.addBatch(gl, vertices, uvs, absoluteHeights, indices, batchRenderUnits);
|
||||
}
|
||||
if (this.splatInstances != null) {
|
||||
for (final SplatMover splatMover : this.splatInstances) {
|
||||
@ -191,25 +200,30 @@ public class SplatModel {
|
||||
}
|
||||
|
||||
private void addBatch(final GL30 gl, final List<float[]> vertices, final List<float[]> uvs,
|
||||
final List<int[]> indices, final List<SplatMover> batchRenderUnits) {
|
||||
final List<float[]> absoluteHeights, final List<int[]> indices, final List<SplatMover> batchRenderUnits) {
|
||||
final int uvsOffset = vertices.size() * 3 * 4;
|
||||
final int paramsOffset = uvsOffset + (uvs.size() * 4 * 2);
|
||||
|
||||
final int vertexBuffer = gl.glGenBuffer();
|
||||
gl.glBindBuffer(GL30.GL_ARRAY_BUFFER, vertexBuffer);
|
||||
gl.glBufferData(GL30.GL_ARRAY_BUFFER, uvsOffset + (uvs.size() * 4 * 2), null, GL30.GL_STATIC_DRAW);
|
||||
gl.glBufferSubData(GL30.GL_ARRAY_BUFFER, 0, vertices.size() * 4 * 5, RenderMathUtils.wrap(vertices));
|
||||
gl.glBufferData(GL30.GL_ARRAY_BUFFER, uvsOffset + (uvs.size() * 4 * 2) + (absoluteHeights.size() * 4), null,
|
||||
GL30.GL_STATIC_DRAW);
|
||||
gl.glBufferSubData(GL30.GL_ARRAY_BUFFER, 0, vertices.size() * 4 * 3, RenderMathUtils.wrap(vertices));
|
||||
gl.glBufferSubData(GL30.GL_ARRAY_BUFFER, uvsOffset, uvs.size() * 4 * 2, RenderMathUtils.wrap(uvs));
|
||||
gl.glBufferSubData(GL30.GL_ARRAY_BUFFER, paramsOffset, absoluteHeights.size() * 4,
|
||||
RenderMathUtils.wrap(absoluteHeights));
|
||||
|
||||
final int faceBuffer = gl.glGenBuffer();
|
||||
gl.glBindBuffer(GL30.GL_ELEMENT_ARRAY_BUFFER, faceBuffer);
|
||||
gl.glBufferData(GL30.GL_ELEMENT_ARRAY_BUFFER, indices.size() * 6 * 2, RenderMathUtils.wrapFaces(indices),
|
||||
GL30.GL_STATIC_DRAW);
|
||||
|
||||
this.batches.add(new Batch(uvsOffset, vertexBuffer, faceBuffer, indices.size() * 6));
|
||||
this.batches.add(new Batch(uvsOffset, vertexBuffer, faceBuffer, indices.size() * 6, paramsOffset));
|
||||
for (final SplatMover mover : batchRenderUnits) {
|
||||
mover.vertexBuffer = vertexBuffer;
|
||||
mover.uvsOffset = uvsOffset;
|
||||
mover.faceBuffer = faceBuffer;
|
||||
mover.absHeightsOffset = paramsOffset;
|
||||
}
|
||||
}
|
||||
|
||||
@ -232,6 +246,7 @@ public class SplatModel {
|
||||
gl.glBindBuffer(GL30.GL_ARRAY_BUFFER, b.vertexBuffer);
|
||||
shader.setVertexAttribute("a_position", 3, GL30.GL_FLOAT, false, 12, 0);
|
||||
shader.setVertexAttribute("a_uv", 2, GL30.GL_FLOAT, false, 8, b.uvsOffset);
|
||||
shader.setVertexAttribute("a_absoluteHeight", 1, GL30.GL_FLOAT, false, 4, b.paramsOffset);
|
||||
|
||||
// Faces.
|
||||
gl.glBindBuffer(GL30.GL_ELEMENT_ARRAY_BUFFER, b.faceBuffer);
|
||||
@ -266,16 +281,20 @@ public class SplatModel {
|
||||
private final int vertexBuffer;
|
||||
private final int faceBuffer;
|
||||
private final int elements;
|
||||
private final int paramsOffset;
|
||||
|
||||
public Batch(final int uvsOffset, final int vertexBuffer, final int faceBuffer, final int elements) {
|
||||
public Batch(final int uvsOffset, final int vertexBuffer, final int faceBuffer, final int elements,
|
||||
final int paramsOffset) {
|
||||
this.uvsOffset = uvsOffset;
|
||||
this.vertexBuffer = vertexBuffer;
|
||||
this.faceBuffer = faceBuffer;
|
||||
this.elements = elements;
|
||||
this.paramsOffset = paramsOffset;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class SplatMover {
|
||||
public int absHeightsOffset;
|
||||
public int faceBuffer;
|
||||
public int uvsOffset;
|
||||
public int iy1;
|
||||
@ -290,11 +309,14 @@ public class SplatModel {
|
||||
private int start;
|
||||
private final List<float[]> vertices = new ArrayList<>();
|
||||
private final List<float[]> uvs = new ArrayList<>();
|
||||
private final List<float[]> absoluteHeights = new ArrayList<>();
|
||||
private final List<int[]> indices = new ArrayList<>();
|
||||
private int indicesStartOffset;
|
||||
private int index;
|
||||
private final SplatModel splatModel;
|
||||
private boolean hidden = false;
|
||||
private boolean heightIsAbsolute = false;
|
||||
private float absoluteHeightValue = 0.0f;
|
||||
|
||||
private SplatMover(final SplatModel splatModel) {
|
||||
this.splatModel = splatModel;
|
||||
@ -307,6 +329,7 @@ public class SplatModel {
|
||||
this.index = index;
|
||||
this.vertices.clear();
|
||||
this.uvs.clear();
|
||||
this.absoluteHeights.clear();
|
||||
this.indices.clear();
|
||||
return this;
|
||||
}
|
||||
@ -423,6 +446,9 @@ public class SplatModel {
|
||||
}
|
||||
gl.glBufferSubData(GL30.GL_ARRAY_BUFFER, this.uvsOffset + ((this.startOffset / 3) * 2),
|
||||
4 * 2 * this.uvs.size(), RenderMathUtils.wrap(this.uvs));
|
||||
if (this.heightIsAbsolute) {
|
||||
updateAbsoluteHeightParams();
|
||||
}
|
||||
}
|
||||
|
||||
public void destroy(final GL30 gl, final float[] centerOffset) {
|
||||
@ -468,5 +494,24 @@ public class SplatModel {
|
||||
move(0, 0, centerOffset);
|
||||
this.hidden = false;
|
||||
}
|
||||
|
||||
public void setHeightAbsolute(final boolean absolute, final float absoluteHeightValue) {
|
||||
this.absoluteHeightValue = absoluteHeightValue;
|
||||
if (absolute != this.heightIsAbsolute) {
|
||||
this.heightIsAbsolute = absolute;
|
||||
updateAbsoluteHeightParams();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateAbsoluteHeightParams() {
|
||||
final GL30 gl = Gdx.gl30;
|
||||
final float height = this.heightIsAbsolute ? this.absoluteHeightValue : NO_ABS_HEIGHT;
|
||||
for (final float[] absHeight : this.absoluteHeights) {
|
||||
absHeight[0] = height;
|
||||
}
|
||||
gl.glBindBuffer(GL30.GL_ARRAY_BUFFER, this.vertexBuffer);
|
||||
gl.glBufferSubData(GL30.GL_ARRAY_BUFFER, this.absHeightsOffset + (this.startOffset / 3),
|
||||
this.absoluteHeights.size() * 4, RenderMathUtils.wrap(this.absoluteHeights));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -144,11 +144,16 @@ public class Variations {
|
||||
}
|
||||
|
||||
public static int getCliffVariation(final String dir, final String tag, final int variation) {
|
||||
final Integer vars;
|
||||
if ("Cliffs".equals(dir)) {
|
||||
return Math.min(variation, CLIFF_VARS.get(tag));
|
||||
vars = CLIFF_VARS.get(tag);
|
||||
}
|
||||
else {
|
||||
return Math.min(variation, CITY_CLIFF_VARS.get(tag));
|
||||
vars = CITY_CLIFF_VARS.get(tag);
|
||||
}
|
||||
if (variation < vars) {
|
||||
return variation;
|
||||
}
|
||||
return variation % (vars + 1);
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ public class W3xShaders {
|
||||
" uniform float u_lightTextureHeight;\r\n" + //
|
||||
" attribute vec3 a_position;\r\n" + //
|
||||
" attribute vec2 a_uv;\r\n" + //
|
||||
" attribute float a_absoluteHeight;\r\n" + //
|
||||
" varying vec2 v_uv;\r\n" + //
|
||||
" varying vec2 v_suv;\r\n" + //
|
||||
" varying vec3 v_normal;\r\n" + //
|
||||
@ -29,19 +30,28 @@ public class W3xShaders {
|
||||
" void main() {\r\n" + //
|
||||
" vec2 halfPixel = u_pixel * 0.5;\r\n" + //
|
||||
" vec2 base = (a_position.xy - u_centerOffset) / 128.0;\r\n" + //
|
||||
" float height = texture2D(u_heightMap, base * u_pixel + halfPixel).r;\r\n" + //
|
||||
" float hL = texture2D(u_heightMap, vec2(base - vec2(normalDist, 0.0)) * u_pixel + halfPixel).r;\r\n"
|
||||
+ //
|
||||
" float hR = texture2D(u_heightMap, vec2(base + vec2(normalDist, 0.0)) * u_pixel + halfPixel).r;\r\n"
|
||||
+ //
|
||||
" float hD = texture2D(u_heightMap, vec2(base - vec2(0.0, normalDist)) * u_pixel + halfPixel).r;\r\n"
|
||||
+ //
|
||||
" float hU = texture2D(u_heightMap, vec2(base + vec2(0.0, normalDist)) * u_pixel + halfPixel).r;\r\n"
|
||||
+ //
|
||||
" float height;\r\n" + //
|
||||
" float hL;\r\n" + //
|
||||
" float hR;\r\n" + //
|
||||
" float hD;\r\n" + //
|
||||
" float hU;\r\n" + //
|
||||
" if (a_absoluteHeight < -256.0) {\r\n" + //
|
||||
" height = texture2D(u_heightMap, base * u_pixel + halfPixel).r * 128.0;\r\n" + //
|
||||
" hL = texture2D(u_heightMap, vec2(base - vec2(normalDist, 0.0)) * u_pixel + halfPixel).r;\r\n" + //
|
||||
" hR = texture2D(u_heightMap, vec2(base + vec2(normalDist, 0.0)) * u_pixel + halfPixel).r;\r\n" + //
|
||||
" hD = texture2D(u_heightMap, vec2(base - vec2(0.0, normalDist)) * u_pixel + halfPixel).r;\r\n" + //
|
||||
" hU = texture2D(u_heightMap, vec2(base + vec2(0.0, normalDist)) * u_pixel + halfPixel).r;\r\n" + //
|
||||
" } else {\r\n" + //
|
||||
" height = a_absoluteHeight;\r\n" + //
|
||||
" hL = a_absoluteHeight;\r\n" + //
|
||||
" hR = a_absoluteHeight;\r\n" + //
|
||||
" hD = a_absoluteHeight;\r\n" + //
|
||||
" hU = a_absoluteHeight;\r\n" + //
|
||||
" }\r\n" + //
|
||||
" v_normal = normalize(vec3(hL - hR, hD - hU, normalDist * 2.0));\r\n" + //
|
||||
" v_uv = a_uv;\r\n" + //
|
||||
" v_suv = base / u_size;\r\n" + //
|
||||
" vec3 myposition = vec3(a_position.xy, height * 128.0 + a_position.z);\r\n" + //
|
||||
" vec3 myposition = vec3(a_position.xy, height + a_position.z);\r\n" + //
|
||||
" gl_Position = u_mvp * vec4(myposition.xyz, 1.0);\r\n" + //
|
||||
" a_positionHeight = a_position.z;\r\n" + //
|
||||
Shaders.lightSystem("v_normal", "myposition", "u_lightTexture", "u_lightTextureHeight", "u_lightCount",
|
||||
|
@ -78,17 +78,20 @@ import com.etheller.warsmash.viewer5.handlers.w3x.environment.Terrain;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.environment.Terrain.Splat;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderAttackInstant;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderAttackProjectile;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderDestructable;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderDoodad;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderEffect;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderItem;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderUnit;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderUnitTypeData;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderWidget;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.ability.AbilityDataUI;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CDestructable;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitClassification;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitFilterFunction;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidgetFilterFunction;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTarget;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttackInstant;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttackMissile;
|
||||
@ -111,7 +114,6 @@ public class War3MapViewer extends AbstractMdxModelViewer {
|
||||
private static final War3ID UNIT_SHADOW_H = War3ID.fromString("ushh");
|
||||
private static final War3ID BUILDING_SHADOW = War3ID.fromString("ushb");
|
||||
public static final War3ID UNIT_SELECT_SCALE = War3ID.fromString("ussc");
|
||||
private static final War3ID UNIT_SELECT_HEIGHT = War3ID.fromString("uslz");
|
||||
private static final War3ID UNIT_SOUNDSET = War3ID.fromString("usnd");
|
||||
private static final War3ID ITEM_FILE = War3ID.fromString("ifil");
|
||||
private static final War3ID UNIT_PATHING = War3ID.fromString("upat");
|
||||
@ -147,6 +149,7 @@ public class War3MapViewer extends AbstractMdxModelViewer {
|
||||
public boolean unitsAndItemsLoaded;
|
||||
public MappedData unitsData = new MappedData();
|
||||
public MappedData unitMetaData = new MappedData();
|
||||
public List<RenderWidget> widgets = new ArrayList<>();
|
||||
public List<RenderUnit> units = new ArrayList<>();
|
||||
public List<RenderItem> items = new ArrayList<>();
|
||||
public List<RenderEffect> projectiles = new ArrayList<>();
|
||||
@ -160,7 +163,7 @@ public class War3MapViewer extends AbstractMdxModelViewer {
|
||||
public int renderLighting = 1;
|
||||
|
||||
public List<SplatModel> selModels = new ArrayList<>();
|
||||
public List<RenderUnit> selected = new ArrayList<>();
|
||||
public List<RenderWidget> selected = new ArrayList<>();
|
||||
private DataTable unitAckSoundsTable;
|
||||
private DataTable unitCombatSoundsTable;
|
||||
public DataTable miscData;
|
||||
@ -336,6 +339,7 @@ public class War3MapViewer extends AbstractMdxModelViewer {
|
||||
this.selectionCircleSizes.add(new SelectionCircleSize(size, texture, textureDotted));
|
||||
}
|
||||
this.selectionCircleScaleFactor = selectionCircleData.getFieldFloatValue("ScaleFactor");
|
||||
this.imageWalkableZOffset = selectionCircleData.getFieldValue("ImageWalkableZOffset");
|
||||
|
||||
this.uiSoundsTable = new DataTable(worldEditStrings);
|
||||
try (InputStream miscDataTxtStream = this.dataSource.getResourceAsStream("UI\\SoundInfo\\UISounds.slk")) {
|
||||
@ -524,7 +528,7 @@ public class War3MapViewer extends AbstractMdxModelViewer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawnUnitDamageSound(final CUnit damagedUnit, final String weaponSound,
|
||||
public void spawnDamageSound(final CWidget damagedDestructable, final String weaponSound,
|
||||
final String armorType) {
|
||||
final String key = weaponSound + armorType;
|
||||
UnitSound combatSound = this.keyToCombatSound.get(key);
|
||||
@ -533,8 +537,8 @@ public class War3MapViewer extends AbstractMdxModelViewer {
|
||||
War3MapViewer.this.unitCombatSoundsTable, weaponSound, armorType);
|
||||
this.keyToCombatSound.put(key, combatSound);
|
||||
}
|
||||
combatSound.play(War3MapViewer.this.worldScene.audioContext, damagedUnit.getX(),
|
||||
damagedUnit.getY());
|
||||
combatSound.play(War3MapViewer.this.worldScene.audioContext, damagedDestructable.getX(),
|
||||
damagedDestructable.getY());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -551,6 +555,7 @@ public class War3MapViewer extends AbstractMdxModelViewer {
|
||||
@Override
|
||||
public void removeUnit(final CUnit unit) {
|
||||
final RenderUnit renderUnit = War3MapViewer.this.unitToRenderPeer.remove(unit);
|
||||
War3MapViewer.this.widgets.remove(renderUnit);
|
||||
War3MapViewer.this.units.remove(renderUnit);
|
||||
War3MapViewer.this.worldScene.removeInstance(renderUnit.instance);
|
||||
}
|
||||
@ -777,10 +782,10 @@ public class War3MapViewer extends AbstractMdxModelViewer {
|
||||
if (type == WorldEditorDataType.DESTRUCTIBLES) {
|
||||
final float x = doodad.getLocation()[0];
|
||||
final float y = doodad.getLocation()[1];
|
||||
this.simulation.createDestructable(row.getAlias(), x, y, destructablePathing,
|
||||
destructablePathingDeath);
|
||||
final CDestructable simulationDestructable = this.simulation.createDestructable(row.getAlias(), x,
|
||||
y, destructablePathing, destructablePathingDeath);
|
||||
final RenderDestructable renderDestructable = new RenderDestructable(this, model, row, doodad, type,
|
||||
maxPitch, maxRoll, doodad.getLife(), destructableShadow);
|
||||
maxPitch, maxRoll, doodad.getLife(), destructableShadow, simulationDestructable);
|
||||
if (row.readSLKTagBoolean("walkable")) {
|
||||
final BoundingBox boundingBox = model.bounds.getBoundingBox();
|
||||
final float minX = boundingBox.min.x + x;
|
||||
@ -792,6 +797,7 @@ public class War3MapViewer extends AbstractMdxModelViewer {
|
||||
renderDestructable.walkableBounds = renderDestructableBounds;
|
||||
}
|
||||
this.doodads.add(renderDestructable);
|
||||
this.widgets.add(renderDestructable);
|
||||
}
|
||||
else {
|
||||
this.doodads.add(new RenderDoodad(this, model, row, doodad, type, maxPitch, maxRoll));
|
||||
@ -1077,8 +1083,10 @@ public class War3MapViewer extends AbstractMdxModelViewer {
|
||||
angle, buildingPathingPixelMap, pathingInstance);
|
||||
final RenderUnitTypeData typeData = getUnitTypeData(unitId, row);
|
||||
final RenderUnit renderUnit = new RenderUnit(this, model, row, unitX, unitY, unitZ, playerIndex,
|
||||
soundset, portraitModel, simulationUnit, typeData, specialArtModel, buildingShadowInstance);
|
||||
soundset, portraitModel, simulationUnit, typeData, specialArtModel, buildingShadowInstance,
|
||||
this.selectionCircleScaleFactor);
|
||||
this.unitToRenderPeer.put(simulationUnit, renderUnit);
|
||||
this.widgets.add(renderUnit);
|
||||
this.units.add(renderUnit);
|
||||
if (unitShadowSplat != null) {
|
||||
unitShadowSplat.unitMapping.add(new Consumer<SplatModel.SplatMover>() {
|
||||
@ -1191,7 +1199,7 @@ public class War3MapViewer extends AbstractMdxModelViewer {
|
||||
|
||||
super.update();
|
||||
|
||||
for (final RenderUnit unit : this.units) {
|
||||
for (final RenderWidget unit : this.widgets) {
|
||||
unit.updateAnimations(this);
|
||||
}
|
||||
final Iterator<RenderEffect> projectileIterator = this.projectiles.iterator();
|
||||
@ -1276,29 +1284,27 @@ public class War3MapViewer extends AbstractMdxModelViewer {
|
||||
this.terrain.removeSplatBatchModel("selection");
|
||||
}
|
||||
this.selModels.clear();
|
||||
for (final RenderUnit unit : this.selected) {
|
||||
unit.selectionCircle = null;
|
||||
for (final RenderWidget unit : this.selected) {
|
||||
unit.unassignSelectionCircle();
|
||||
}
|
||||
}
|
||||
this.selected.clear();
|
||||
}
|
||||
|
||||
public void doSelectUnit(final List<RenderUnit> units) {
|
||||
public void doSelectUnit(final List<RenderWidget> units) {
|
||||
deselect();
|
||||
if (units.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Map<String, Terrain.Splat> splats = new HashMap<String, Terrain.Splat>();
|
||||
for (final RenderUnit unit : units) {
|
||||
if (unit.row != null) {
|
||||
if (unit.selectionScale > 0) {
|
||||
final float selectionSize = unit.selectionScale * this.selectionCircleScaleFactor;
|
||||
for (final RenderWidget unit : units) {
|
||||
if (unit.getSelectionScale() > 0) {
|
||||
final float selectionSize = unit.getSelectionScale();
|
||||
String path = null;
|
||||
for (int i = 0; i < this.selectionCircleSizes.size(); i++) {
|
||||
final SelectionCircleSize selectionCircleSize = this.selectionCircleSizes.get(i);
|
||||
if ((selectionSize < selectionCircleSize.size)
|
||||
|| (i == (this.selectionCircleSizes.size() - 1))) {
|
||||
if ((selectionSize < selectionCircleSize.size) || (i == (this.selectionCircleSizes.size() - 1))) {
|
||||
path = selectionCircleSize.texture;
|
||||
break;
|
||||
}
|
||||
@ -1309,25 +1315,24 @@ public class War3MapViewer extends AbstractMdxModelViewer {
|
||||
if (!splats.containsKey(path)) {
|
||||
splats.put(path, new Splat());
|
||||
}
|
||||
final float x = unit.location[0];
|
||||
final float y = unit.location[1];
|
||||
final float x = unit.getX();
|
||||
final float y = unit.getY();
|
||||
System.out.println("Selecting a unit at " + x + "," + y);
|
||||
final float z = unit.row.getFieldAsFloat(UNIT_SELECT_HEIGHT, 0);
|
||||
final float z = unit.getSelectionHeight();
|
||||
splats.get(path).locations.add(new float[] { x - (selectionSize / 2), y - (selectionSize / 2),
|
||||
x + (selectionSize / 2), y + (selectionSize / 2), z + 5 });
|
||||
splats.get(path).unitMapping.add(new Consumer<SplatModel.SplatMover>() {
|
||||
@Override
|
||||
public void accept(final SplatMover t) {
|
||||
unit.selectionCircle = t;
|
||||
if (unit.instance.hidden()) {
|
||||
unit.selectionCircle.hide();
|
||||
unit.assignSelectionCircle(t);
|
||||
if (unit.getInstance().hidden()) {
|
||||
t.hide();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
this.selected.add(unit);
|
||||
}
|
||||
}
|
||||
this.selModels.clear();
|
||||
for (final Map.Entry<String, Terrain.Splat> entry : splats.entrySet()) {
|
||||
final String path = entry.getKey();
|
||||
@ -1372,10 +1377,10 @@ public class War3MapViewer extends AbstractMdxModelViewer {
|
||||
this.confirmationInstance.vertexColor[2] = blue;
|
||||
}
|
||||
|
||||
public List<RenderUnit> selectUnit(final float x, final float y, final boolean toggle) {
|
||||
public List<RenderWidget> selectUnit(final float x, final float y, final boolean toggle) {
|
||||
System.out.println("world: " + x + "," + y);
|
||||
final RenderUnit entity = rayPickUnit(x, y, CUnitFilterFunction.ACCEPT_ALL_LIVING);
|
||||
List<RenderUnit> sel;
|
||||
final RenderWidget entity = rayPickUnit(x, y, CWidgetFilterFunction.ACCEPT_ALL_LIVING);
|
||||
List<RenderWidget> sel;
|
||||
if (entity != null) {
|
||||
if (toggle) {
|
||||
sel = new ArrayList<>(this.selected);
|
||||
@ -1398,25 +1403,25 @@ public class War3MapViewer extends AbstractMdxModelViewer {
|
||||
return sel;
|
||||
}
|
||||
|
||||
public RenderUnit rayPickUnit(final float x, final float y) {
|
||||
return rayPickUnit(x, y, CUnitFilterFunction.ACCEPT_ALL);
|
||||
public RenderWidget rayPickUnit(final float x, final float y) {
|
||||
return rayPickUnit(x, y, CWidgetFilterFunction.ACCEPT_ALL);
|
||||
}
|
||||
|
||||
public RenderUnit rayPickUnit(final float x, final float y, final CUnitFilterFunction filter) {
|
||||
public RenderWidget rayPickUnit(final float x, final float y, final CWidgetFilterFunction filter) {
|
||||
final float[] ray = rayHeap;
|
||||
mousePosHeap.set(x, y);
|
||||
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
|
||||
|
||||
RenderUnit entity = null;
|
||||
for (final RenderUnit unit : this.units) {
|
||||
final MdxComplexInstance instance = unit.instance;
|
||||
if (instance.shown() && instance.isVisible(this.worldScene.camera) && instance.intersectRayWithCollision(
|
||||
gdxRayHeap, intersectionHeap, unit.getSimulationUnit().getUnitType().isBuilding(), false)) {
|
||||
if (filter.call(unit.getSimulationUnit()) && (intersectionHeap.z > this.terrain
|
||||
RenderWidget entity = null;
|
||||
for (final RenderWidget unit : this.widgets) {
|
||||
final MdxComplexInstance instance = unit.getInstance();
|
||||
if (instance.shown() && instance.isVisible(this.worldScene.camera) && instance
|
||||
.intersectRayWithCollision(gdxRayHeap, intersectionHeap, unit.isIntersectedOnMeshAlways(), false)) {
|
||||
if (filter.call(unit.getSimulationWidget()) && (intersectionHeap.z > this.terrain
|
||||
.getGroundHeight(intersectionHeap.x, intersectionHeap.y))) {
|
||||
if ((entity == null) || (entity.instance.depth > instance.depth)) {
|
||||
if ((entity == null) || (entity.getInstance().depth > instance.depth)) {
|
||||
entity = unit;
|
||||
}
|
||||
}
|
||||
@ -1495,6 +1500,7 @@ public class War3MapViewer extends AbstractMdxModelViewer {
|
||||
private Warcraft3MapObjectData allObjectData;
|
||||
private AbilityDataUI abilityDataUI;
|
||||
private Map<String, UnitSoundset> soundsetNameToSoundset;
|
||||
public int imageWalkableZOffset;
|
||||
|
||||
/**
|
||||
* Returns a power of two size for the given target capacity.
|
||||
|
@ -244,7 +244,7 @@ public class Terrain {
|
||||
// Cliff Textures
|
||||
for (final War3ID cliffTile : w3eFile.getCliffTiles()) {
|
||||
final Element cliffInfo = this.cliffTable.get(cliffTile.asStringValue());
|
||||
if(cliffInfo == null) {
|
||||
if (cliffInfo == null) {
|
||||
System.err.println("Missing cliff type: " + cliffTile.asStringValue());
|
||||
continue;
|
||||
}
|
||||
@ -656,10 +656,10 @@ public class Terrain {
|
||||
|
||||
// Cliff model path
|
||||
|
||||
String fileName = "" + (char) (('A' + bottomLeft.getLayerHeight()) - base)
|
||||
+ (char) (('A' + topLeft.getLayerHeight()) - base)
|
||||
String fileName = "" + (char) (('A' + topLeft.getLayerHeight()) - base)
|
||||
+ (char) (('A' + topRight.getLayerHeight()) - base)
|
||||
+ (char) (('A' + bottomRight.getLayerHeight()) - base);
|
||||
+ (char) (('A' + bottomRight.getLayerHeight()) - base)
|
||||
+ (char) (('A' + bottomLeft.getLayerHeight()) - base);
|
||||
|
||||
if ("AAAA".equals(fileName)) {
|
||||
continue;
|
||||
|
@ -35,15 +35,16 @@ public class TerrainShaders {
|
||||
"out vec3 shadeColor;\r\n" + //
|
||||
"\r\n" + //
|
||||
"void main() {\r\n" + //
|
||||
" pathing_map_uv = (vec2(vPosition.x + 128, vPosition.y) / 128 + vOffset.xy) * 4;\r\n" + //
|
||||
" pathing_map_uv = (vec2(vPosition.y, -vPosition.x) / 128 + vOffset.xy) * 4;\r\n" + //
|
||||
" \r\n" + //
|
||||
" ivec2 size = textureSize(height_texture, 0);\r\n" + //
|
||||
" ivec2 shadowSize = textureSize(shadowMap, 0);\r\n" + //
|
||||
" v_suv = pathing_map_uv / shadowSize;\r\n" + //
|
||||
" float value = texture(height_texture, (vOffset.xy + vec2(vPosition.x + 192, vPosition.y + 64) / 128.0) / vec2(size)).r;\r\n"
|
||||
" float value = texture(height_texture, (vOffset.xy + vec2(vPosition.y + 64, -vPosition.x + 64) / 128.0) / vec2(size)).r;\r\n"
|
||||
+ //
|
||||
"\r\n" + //
|
||||
" position = (vPosition + vec3(vOffset.xy + vec2(1, 0), vOffset.z + value) * 128 );\r\n" + //
|
||||
" position = (vec3(vPosition.y, -vPosition.x, vPosition.z) + vec3(vOffset.xy, vOffset.z + value) * 128 );\r\n"
|
||||
+ //
|
||||
" vec4 myposition = vec4(position, 1);\r\n" + //
|
||||
" myposition.x += centerOffsetX;\r\n" + //
|
||||
" myposition.y += centerOffsetY;\r\n" + //
|
||||
@ -52,13 +53,14 @@ public class TerrainShaders {
|
||||
" gl_Position = MVP * myposition;\r\n" + //
|
||||
" UV = vec3(vUV, vOffset.a);\r\n" + //
|
||||
"\r\n" + //
|
||||
" ivec2 height_pos = ivec2(vOffset.xy + vec2(vPosition.x + 128, vPosition.y) / 128);\r\n" + //
|
||||
" ivec2 height_pos = ivec2(vOffset.xy + vec2(vPosition.y, -vPosition.x) / 128);\r\n" + //
|
||||
" ivec3 off = ivec3(1, 1, 0);\r\n" + //
|
||||
" float hL = texelFetch(height_texture, height_pos - off.xz, 0).r;\r\n" + //
|
||||
" float hR = texelFetch(height_texture, height_pos + off.xz, 0).r;\r\n" + //
|
||||
" float hD = texelFetch(height_texture, height_pos - off.zy, 0).r;\r\n" + //
|
||||
" float hU = texelFetch(height_texture, height_pos + off.zy, 0).r;\r\n" + //
|
||||
" vec3 terrain_normal = normalize(vNormal);//vec3(hL - hR, hD - hU, 2.0)+vNormal);\r\n" + //
|
||||
" vec3 terrain_normal = normalize(vec3(vNormal.y, -vNormal.x, vNormal.z));//vec3(hL - hR, hD - hU, 2.0)+vNormal);\r\n"
|
||||
+ //
|
||||
"\r\n" + //
|
||||
" Normal = terrain_normal;\r\n" + //
|
||||
Shaders.lightSystem("terrain_normal", "myposition.xyz", "lightTexture", "lightTextureHeight",
|
||||
|
@ -0,0 +1,105 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x.rendersim;
|
||||
|
||||
import com.badlogic.gdx.math.Rectangle;
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject;
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData.WorldEditorDataType;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.SplatModel.SplatMover;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.War3MapViewer;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.environment.BuildingShadow;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CDestructable;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
|
||||
|
||||
public class RenderDestructable extends RenderDoodad implements RenderWidget {
|
||||
private static final War3ID TEX_FILE = War3ID.fromString("btxf");
|
||||
private static final War3ID TEX_ID = War3ID.fromString("btxi");
|
||||
private static final War3ID SEL_CIRCLE_SIZE = War3ID.fromString("bgsc");
|
||||
|
||||
private final float life;
|
||||
public Rectangle walkableBounds;
|
||||
private final CDestructable simulationDestructable;
|
||||
private SplatMover selectionCircle;
|
||||
|
||||
public RenderDestructable(final War3MapViewer map, final MdxModel model, final MutableGameObject row,
|
||||
final com.etheller.warsmash.parsers.w3x.doo.Doodad doodad, final WorldEditorDataType type,
|
||||
final float maxPitch, final float maxRoll, final float life, final BuildingShadow destructableShadow,
|
||||
final CDestructable simulationDestructable) {
|
||||
super(map, model, row, doodad, type, maxPitch, maxRoll);
|
||||
this.life = life;
|
||||
this.simulationDestructable = simulationDestructable;
|
||||
String replaceableTextureFile = row.getFieldAsString(TEX_FILE, 0);
|
||||
final int replaceableTextureId = row.getFieldAsInteger(TEX_ID, 0);
|
||||
if ((replaceableTextureFile != null) && (replaceableTextureFile.length() > 1)) {
|
||||
final int dotIndex = replaceableTextureFile.lastIndexOf('.');
|
||||
if (dotIndex != -1) {
|
||||
replaceableTextureFile = replaceableTextureFile.substring(0, dotIndex);
|
||||
}
|
||||
replaceableTextureFile += ".blp";
|
||||
this.instance.setReplaceableTexture(replaceableTextureId, replaceableTextureFile);
|
||||
}
|
||||
this.selectionScale *= row.getFieldAsFloat(SEL_CIRCLE_SIZE, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrimaryTag getAnimation() {
|
||||
if (this.life <= 0) {
|
||||
return PrimaryTag.DEATH;
|
||||
}
|
||||
return super.getAnimation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MdxComplexInstance getInstance() {
|
||||
return (MdxComplexInstance) this.instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CWidget getSimulationWidget() {
|
||||
return this.simulationDestructable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAnimations(final War3MapViewer war3MapViewer) {
|
||||
// TODO maybe move getAnimation behaviors to here and make this thing not a
|
||||
// doodad
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIntersectedOnMeshAlways() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getSelectionScale() {
|
||||
return this.selectionScale;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getX() {
|
||||
return this.x;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getY() {
|
||||
return this.y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getSelectionHeight() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unassignSelectionCircle() {
|
||||
this.selectionCircle = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void assignSelectionCircle(final SplatMover selectionCircle) {
|
||||
this.selectionCircle = selectionCircle;
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x.rendersim;
|
||||
|
||||
import com.badlogic.gdx.math.Quaternion;
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject;
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData.WorldEditorDataType;
|
||||
import com.etheller.warsmash.util.RenderMathUtils;
|
||||
import com.etheller.warsmash.viewer5.ModelInstance;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.SequenceLoopMode;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.War3MapViewer;
|
||||
|
||||
public class RenderDoodad {
|
||||
private static final int SAMPLE_RADIUS = 4;
|
||||
public final ModelInstance instance;
|
||||
private final MutableGameObject row;
|
||||
private final float maxPitch;
|
||||
private final float maxRoll;
|
||||
protected float x;
|
||||
protected float y;
|
||||
protected float selectionScale;
|
||||
|
||||
public RenderDoodad(final War3MapViewer map, final MdxModel model, final MutableGameObject row,
|
||||
final com.etheller.warsmash.parsers.w3x.doo.Doodad doodad, final WorldEditorDataType type,
|
||||
final float maxPitch, final float maxRoll) {
|
||||
this.maxPitch = maxPitch;
|
||||
this.maxRoll = maxRoll;
|
||||
final boolean isSimple = row.readSLKTagBoolean("lightweight");
|
||||
ModelInstance instance;
|
||||
|
||||
if (isSimple && false) {
|
||||
instance = model.addInstance(1);
|
||||
}
|
||||
else {
|
||||
instance = model.addInstance();
|
||||
((MdxComplexInstance) instance).setSequenceLoopMode(SequenceLoopMode.NEVER_LOOP);
|
||||
}
|
||||
|
||||
instance.move(doodad.getLocation());
|
||||
// TODO: the following pitch/roll system is a heuristic, and we probably want to
|
||||
// revisit it later.
|
||||
// Specifically, I was pretty convinced that whichever is applied first
|
||||
// (pitch/roll) should be used to do a projection onto the already-tilted plane
|
||||
// to find the angle used for the other of the two
|
||||
// (instead of measuring down from an imaginary flat ground plane, as we do
|
||||
// currently).
|
||||
final float facingRadians = doodad.getAngle();
|
||||
float pitch, roll;
|
||||
this.x = doodad.getLocation()[0];
|
||||
this.y = doodad.getLocation()[1];
|
||||
final float pitchSampleForwardX = this.x + (SAMPLE_RADIUS * (float) Math.cos(facingRadians));
|
||||
final float pitchSampleForwardY = this.y + (SAMPLE_RADIUS * (float) Math.sin(facingRadians));
|
||||
final float pitchSampleBackwardX = this.x - (SAMPLE_RADIUS * (float) Math.cos(facingRadians));
|
||||
final float pitchSampleBackwardY = this.y - (SAMPLE_RADIUS * (float) Math.sin(facingRadians));
|
||||
final float pitchSampleGroundHeight1 = map.terrain.getGroundHeight(pitchSampleBackwardX, pitchSampleBackwardY);
|
||||
final float pitchSampleGorundHeight2 = map.terrain.getGroundHeight(pitchSampleForwardX, pitchSampleForwardY);
|
||||
pitch = Math.max(-maxPitch, Math.min(maxPitch,
|
||||
(float) Math.atan2(pitchSampleGorundHeight2 - pitchSampleGroundHeight1, SAMPLE_RADIUS * 2)));
|
||||
final double leftOfFacingAngle = facingRadians + (Math.PI / 2);
|
||||
final float rollSampleForwardX = this.x + (SAMPLE_RADIUS * (float) Math.cos(leftOfFacingAngle));
|
||||
final float rollSampleForwardY = this.y + (SAMPLE_RADIUS * (float) Math.sin(leftOfFacingAngle));
|
||||
final float rollSampleBackwardX = this.x - (SAMPLE_RADIUS * (float) Math.cos(leftOfFacingAngle));
|
||||
final float rollSampleBackwardY = this.y - (SAMPLE_RADIUS * (float) Math.sin(leftOfFacingAngle));
|
||||
final float rollSampleGroundHeight1 = map.terrain.getGroundHeight(rollSampleBackwardX, rollSampleBackwardY);
|
||||
final float rollSampleGroundHeight2 = map.terrain.getGroundHeight(rollSampleForwardX, rollSampleForwardY);
|
||||
roll = Math.max(-maxRoll, Math.min(maxRoll,
|
||||
(float) Math.atan2(rollSampleGroundHeight2 - rollSampleGroundHeight1, SAMPLE_RADIUS * 2)));
|
||||
instance.rotate(new Quaternion().setFromAxisRad(RenderMathUtils.VEC3_UNIT_Z, facingRadians));
|
||||
instance.rotate(new Quaternion().setFromAxisRad(RenderMathUtils.VEC3_UNIT_Y, -pitch));
|
||||
instance.rotate(new Quaternion().setFromAxisRad(RenderMathUtils.VEC3_UNIT_X, roll));
|
||||
// instance.rotate(new Quaternion().setEulerAnglesRad(facingRadians, 0, 0));
|
||||
final float[] scale = doodad.getScale();
|
||||
instance.scale(scale);
|
||||
if (type == WorldEditorDataType.DOODADS) {
|
||||
final float defScale = row.readSLKTagFloat("defScale");
|
||||
instance.uniformScale(defScale);
|
||||
this.selectionScale = defScale;
|
||||
}
|
||||
else {
|
||||
this.selectionScale = (float) Math.sqrt((scale[0]) * (scale[1]) * (scale[2]));
|
||||
}
|
||||
instance.setScene(map.worldScene);
|
||||
|
||||
this.instance = instance;
|
||||
this.row = row;
|
||||
}
|
||||
|
||||
public PrimaryTag getAnimation() {
|
||||
return PrimaryTag.STAND;
|
||||
}
|
||||
}
|
@ -28,9 +28,10 @@ import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.commandbuttons.Comma
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitAnimationListener;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbility;
|
||||
|
||||
public class RenderUnit {
|
||||
public class RenderUnit implements RenderWidget {
|
||||
public static final Quaternion tempQuat = new Quaternion();
|
||||
private static final War3ID RED = War3ID.fromString("uclr");
|
||||
private static final War3ID GREEN = War3ID.fromString("uclg");
|
||||
@ -41,6 +42,7 @@ public class RenderUnit {
|
||||
private static final War3ID ANIM_PROPS = War3ID.fromString("uani");
|
||||
private static final War3ID BLEND_TIME = War3ID.fromString("uble");
|
||||
private static final War3ID BUILD_SOUND_LABEL = War3ID.fromString("ubsl");
|
||||
private static final War3ID UNIT_SELECT_HEIGHT = War3ID.fromString("uslz");
|
||||
private static final float[] heapZ = new float[3];
|
||||
public final MdxComplexInstance instance;
|
||||
public final MutableGameObject row;
|
||||
@ -74,7 +76,8 @@ public class RenderUnit {
|
||||
public RenderUnit(final War3MapViewer map, final MdxModel model, final MutableGameObject row, final float x,
|
||||
final float y, final float z, final int playerIndex, final UnitSoundset soundset,
|
||||
final MdxModel portraitModel, final CUnit simulationUnit, final RenderUnitTypeData typeData,
|
||||
final MdxModel specialArtModel, final BuildingShadow buildingShadow) {
|
||||
final MdxModel specialArtModel, final BuildingShadow buildingShadow,
|
||||
final float selectionCircleScaleFactor) {
|
||||
this.portraitModel = portraitModel;
|
||||
this.simulationUnit = simulationUnit;
|
||||
this.typeData = typeData;
|
||||
@ -123,7 +126,7 @@ public class RenderUnit {
|
||||
(row.getFieldAsInteger(green, 0)) / 255f, (row.getFieldAsInteger(blue, 0)) / 255f });
|
||||
instance.uniformScale(row.getFieldAsFloat(scale, 0));
|
||||
|
||||
this.selectionScale = row.getFieldAsFloat(War3MapViewer.UNIT_SELECT_SCALE, 0);
|
||||
this.selectionScale = row.getFieldAsFloat(War3MapViewer.UNIT_SELECT_SCALE, 0) * selectionCircleScaleFactor;
|
||||
int orientationInterpolationOrdinal = row.getFieldAsInteger(ORIENTATION_INTERPOLATION, 0);
|
||||
if ((orientationInterpolationOrdinal < 0)
|
||||
|| (orientationInterpolationOrdinal >= OrientationInterpolation.VALUES.length)) {
|
||||
@ -150,6 +153,7 @@ public class RenderUnit {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAnimations(final War3MapViewer map) {
|
||||
final boolean wasHidden = this.instance.hidden();
|
||||
if (this.simulationUnit.isHidden()) {
|
||||
@ -390,9 +394,12 @@ public class RenderUnit {
|
||||
map.worldScene.instanceMoved(this.instance, this.location[0], this.location[1]);
|
||||
if (this.shadow != null) {
|
||||
this.shadow.move(dx, dy, map.terrain.centerOffset);
|
||||
this.shadow.setHeightAbsolute(currentWalkableUnder != null, this.location[2] + map.imageWalkableZOffset);
|
||||
}
|
||||
if (this.selectionCircle != null) {
|
||||
this.selectionCircle.move(dx, dy, map.terrain.centerOffset);
|
||||
this.selectionCircle.setHeightAbsolute(currentWalkableUnder != null,
|
||||
this.location[2] + map.imageWalkableZOffset);
|
||||
}
|
||||
this.unitAnimationListenerImpl.update();
|
||||
if (!dead && this.simulationUnit.isConstructing()) {
|
||||
@ -553,4 +560,49 @@ public class RenderUnit {
|
||||
this.location[0] = this.simulationUnit.getX();
|
||||
this.location[1] = this.simulationUnit.getY();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MdxComplexInstance getInstance() {
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CWidget getSimulationWidget() {
|
||||
return this.simulationUnit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIntersectedOnMeshAlways() {
|
||||
return this.simulationUnit.getUnitType().isBuilding();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getSelectionHeight() {
|
||||
return this.row.getFieldAsFloat(UNIT_SELECT_HEIGHT, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getSelectionScale() {
|
||||
return this.selectionScale;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getX() {
|
||||
return this.location[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getY() {
|
||||
return this.location[1];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unassignSelectionCircle() {
|
||||
this.selectionCircle = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void assignSelectionCircle(final SplatMover t) {
|
||||
this.selectionCircle = t;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,28 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x.rendersim;
|
||||
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.SplatModel.SplatMover;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.War3MapViewer;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
|
||||
|
||||
public interface RenderWidget {
|
||||
MdxComplexInstance getInstance();
|
||||
|
||||
CWidget getSimulationWidget();
|
||||
|
||||
void updateAnimations(War3MapViewer war3MapViewer);
|
||||
|
||||
boolean isIntersectedOnMeshAlways();
|
||||
|
||||
float getSelectionScale();
|
||||
|
||||
float getX();
|
||||
|
||||
float getY();
|
||||
|
||||
float getSelectionHeight();
|
||||
|
||||
void unassignSelectionCircle();
|
||||
|
||||
void assignSelectionCircle(SplatMover t);
|
||||
}
|
@ -36,6 +36,7 @@ public class CDestructable extends CWidget {
|
||||
public void damage(final CSimulation simulation, final CUnit source, final CAttackType attackType,
|
||||
final String weaponType, final float damage) {
|
||||
this.life -= damage;
|
||||
simulation.destructableDamageEvent(this, weaponType, this.destType.getArmorType());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -50,7 +51,7 @@ public class CDestructable extends CWidget {
|
||||
}
|
||||
}
|
||||
else {
|
||||
System.err.println("No targeting because " + targetsAllowed + " does not contain all of "
|
||||
System.err.println("Not targeting because " + targetsAllowed + " does not contain all of "
|
||||
+ this.destType.getTargetedAs());
|
||||
}
|
||||
return false;
|
||||
@ -60,4 +61,8 @@ public class CDestructable extends CWidget {
|
||||
public <T> T visit(final AbilityTargetVisitor<T> visitor) {
|
||||
return visitor.accept(this);
|
||||
}
|
||||
|
||||
public CDestructableType getDestType() {
|
||||
return this.destType;
|
||||
}
|
||||
}
|
||||
|
@ -239,7 +239,12 @@ public class CSimulation {
|
||||
}
|
||||
|
||||
public void unitDamageEvent(final CUnit damagedUnit, final String weaponSound, final String armorType) {
|
||||
this.simulationRenderController.spawnUnitDamageSound(damagedUnit, weaponSound, armorType);
|
||||
this.simulationRenderController.spawnDamageSound(damagedUnit, weaponSound, armorType);
|
||||
}
|
||||
|
||||
public void destructableDamageEvent(final CDestructable damagedDestructable, final String weaponSound,
|
||||
final String armorType) {
|
||||
this.simulationRenderController.spawnDamageSound(damagedDestructable, weaponSound, armorType);
|
||||
}
|
||||
|
||||
public void unitConstructedEvent(final CUnit constructingUnit, final CUnit constructedStructure) {
|
||||
@ -284,4 +289,16 @@ public class CSimulation {
|
||||
player.setUnitFoodMade(unit, unit.getUnitType().getFoodMade());
|
||||
}
|
||||
}
|
||||
|
||||
public CWidget getWidget(final int handleId) {
|
||||
final CUnit unit = this.handleIdToUnit.get(handleId);
|
||||
if (unit != null) {
|
||||
return unit;
|
||||
}
|
||||
final CDestructable destructable = this.handleIdToDestructable.get(handleId);
|
||||
if (destructable != null) {
|
||||
return destructable;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +0,0 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x.simulation;
|
||||
|
||||
public interface CUnitFilterFunction {
|
||||
boolean call(CUnit unit);
|
||||
|
||||
CUnitFilterFunction ACCEPT_ALL = new CUnitFilterFunction() {
|
||||
@Override
|
||||
public boolean call(final CUnit unit) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
CUnitFilterFunction ACCEPT_ALL_LIVING = new CUnitFilterFunction() {
|
||||
@Override
|
||||
public boolean call(final CUnit unit) {
|
||||
return !unit.isDead();
|
||||
}
|
||||
};
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x.simulation;
|
||||
|
||||
public interface CWidgetFilterFunction {
|
||||
boolean call(CWidget unit);
|
||||
|
||||
CWidgetFilterFunction ACCEPT_ALL = new CWidgetFilterFunction() {
|
||||
@Override
|
||||
public boolean call(final CWidget unit) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
CWidgetFilterFunction ACCEPT_ALL_LIVING = new CWidgetFilterFunction() {
|
||||
@Override
|
||||
public boolean call(final CWidget unit) {
|
||||
return !unit.isDead();
|
||||
}
|
||||
};
|
||||
}
|
@ -47,8 +47,10 @@ public class CBehaviorAttack extends CAbstractRangedBehavior {
|
||||
if (simulation.getGameTurnTick() < this.unit.getCooldownEndTime()) {
|
||||
range += this.unitAttack.getRangeMotionBuffer();
|
||||
}
|
||||
final double rangeCheckDistance = this.unit.distance(this.target);
|
||||
System.out.println("rangeCheckDistance=" + rangeCheckDistance);
|
||||
return this.unit.canReach(this.target, range)
|
||||
&& (this.unit.distance(this.target) >= this.unit.getUnitType().getMinimumAttackRange());
|
||||
&& (rangeCheckDistance >= this.unit.getUnitType().getMinimumAttackRange());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -123,8 +123,7 @@ public class CUnitAttackMissileSplash extends CUnitAttackMissile {
|
||||
this.y = y;
|
||||
this.damage = damage;
|
||||
this.hitTarget = false;
|
||||
final float doubleMaxArea = attack.areaOfEffectSmallDamage
|
||||
+ (this.simulation.getGameplayConstants().getCloseEnoughRange() * 2);
|
||||
final float doubleMaxArea = (attack.areaOfEffectSmallDamage) * 2;
|
||||
final float maxArea = doubleMaxArea / 2;
|
||||
this.rect.set(x - maxArea, y - maxArea, doubleMaxArea, doubleMaxArea);
|
||||
simulation.getWorldCollision().enumUnitsInRect(this.rect, this);
|
||||
@ -133,17 +132,17 @@ public class CUnitAttackMissileSplash extends CUnitAttackMissile {
|
||||
@Override
|
||||
public boolean call(final CUnit enumUnit) {
|
||||
if (enumUnit.canBeTargetedBy(this.simulation, this.source, this.attack.areaOfEffectTargets)) {
|
||||
final double distance = enumUnit.distance(this.x, this.y)
|
||||
- this.simulation.getGameplayConstants().getCloseEnoughRange();
|
||||
if (distance <= (this.attack.areaOfEffectFullDamage / 2)) {
|
||||
final double distance = enumUnit.distance(this.x, this.y);
|
||||
System.out.println("enum distance=" + distance);
|
||||
if (distance <= (this.attack.areaOfEffectFullDamage)) {
|
||||
enumUnit.damage(this.simulation, this.source, this.attack.getAttackType(),
|
||||
this.attack.getWeaponSound(), this.damage);
|
||||
}
|
||||
else if (distance <= (this.attack.areaOfEffectMediumDamage / 2)) {
|
||||
else if (distance <= (this.attack.areaOfEffectMediumDamage)) {
|
||||
enumUnit.damage(this.simulation, this.source, this.attack.getAttackType(),
|
||||
this.attack.getWeaponSound(), this.damage * this.attack.damageFactorMedium);
|
||||
}
|
||||
else if (distance <= (this.attack.areaOfEffectSmallDamage / 2)) {
|
||||
else if (distance <= (this.attack.areaOfEffectSmallDamage)) {
|
||||
enumUnit.damage(this.simulation, this.source, this.attack.getAttackType(),
|
||||
this.attack.getWeaponSound(), this.damage * this.attack.damageFactorSmall);
|
||||
}
|
||||
|
@ -47,12 +47,8 @@ public class CAttackProjectile {
|
||||
final float d1y = dtsy / c;
|
||||
|
||||
float travelDistance = Math.min(c, this.speed * WarsmashConstants.SIMULATION_STEP_TIME);
|
||||
if (c <= travelDistance) {
|
||||
if (!this.done) {
|
||||
this.unitAttack.doDamage(cSimulation, this.source, this.target, this.damage, this.x, this.y,
|
||||
this.bounceIndex);
|
||||
}
|
||||
this.done = true;
|
||||
final boolean done = c <= travelDistance;
|
||||
if (done) {
|
||||
travelDistance = c;
|
||||
}
|
||||
|
||||
@ -62,6 +58,11 @@ public class CAttackProjectile {
|
||||
this.x = this.x + dx;
|
||||
this.y = this.y + dy;
|
||||
|
||||
if (done && !this.done) {
|
||||
this.unitAttack.doDamage(cSimulation, this.source, this.target, this.damage, this.x, this.y,
|
||||
this.bounceIndex);
|
||||
this.done = true;
|
||||
}
|
||||
return this.done;
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ public class COrderTargetWidget implements COrder {
|
||||
|
||||
@Override
|
||||
public AbilityTarget getTarget(final CSimulation game) {
|
||||
final CUnit target = game.getUnit(this.targetHandleId);
|
||||
final CWidget target = game.getWidget(this.targetHandleId);
|
||||
return target;
|
||||
}
|
||||
|
||||
@ -48,7 +48,7 @@ public class COrderTargetWidget implements COrder {
|
||||
final CAbility ability = game.getAbility(this.abilityHandleId);
|
||||
ability.checkCanUse(game, caster, this.orderId, abilityActivationReceiver.reset());
|
||||
if (abilityActivationReceiver.isUseOk()) {
|
||||
final CUnit target = game.getUnit(this.targetHandleId);
|
||||
final CWidget target = game.getWidget(this.targetHandleId);
|
||||
final StringMsgTargetCheckReceiver<CWidget> targetReceiver = (StringMsgTargetCheckReceiver<CWidget>) targetCheckReceiver;
|
||||
ability.checkCanTarget(game, caster, this.orderId, target, targetReceiver);
|
||||
if (targetReceiver.getTarget() != null) {
|
||||
|
@ -21,7 +21,7 @@ public interface SimulationRenderController {
|
||||
|
||||
void createInstantAttackEffect(CSimulation cSimulation, CUnit source, CUnitAttackInstant attack, CWidget target);
|
||||
|
||||
void spawnUnitDamageSound(CUnit damagedUnit, final String weaponSound, String armorType);
|
||||
void spawnDamageSound(CWidget damagedDestructable, String weaponSound, String armorType);
|
||||
|
||||
void spawnUnitConstructionSound(CUnit constructingUnit, CUnit constructedStructure);
|
||||
|
||||
|
@ -76,6 +76,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.camera.GameCameraManager;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.camera.PortraitCameraManager;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.environment.PathingGrid.PathingFlags;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderUnit;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderWidget;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.ability.AbilityDataUI;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.ability.IconUI;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.commandbuttons.CommandButtonListener;
|
||||
@ -85,10 +86,10 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CPlayerStateListene
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit.QueueItemType;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitClassification;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitFilterFunction;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitStateListener;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitType;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidgetFilterFunction;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbility;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityAttack;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityGeneric;
|
||||
@ -252,6 +253,7 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
|
||||
private MeleeUIAbilityActivationReceiver meleeUIAbilityActivationReceiver;
|
||||
private MdxModel waypointModel;
|
||||
private final List<MdxComplexInstance> waypointModelInstances = new ArrayList<>();
|
||||
private List<RenderUnit> selectedUnits;
|
||||
|
||||
public MeleeUI(final DataSource dataSource, final ExtendViewport uiViewport,
|
||||
final FreeTypeFontGenerator fontGenerator, final Scene uiScene, final Scene portraitScene,
|
||||
@ -1157,9 +1159,9 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
|
||||
}
|
||||
}
|
||||
|
||||
private final class ActiveCommandUnitTargetFilter implements CUnitFilterFunction {
|
||||
private final class ActiveCommandUnitTargetFilter implements CWidgetFilterFunction {
|
||||
@Override
|
||||
public boolean call(final CUnit unit) {
|
||||
public boolean call(final CWidget unit) {
|
||||
final BooleanAbilityTargetCheckReceiver<CWidget> targetReceiver = BooleanAbilityTargetCheckReceiver
|
||||
.<CWidget>getInstance();
|
||||
MeleeUI.this.activeCommand.checkCanTarget(MeleeUI.this.war3MapViewer.simulation,
|
||||
@ -1735,16 +1737,18 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
|
||||
clearAndRepopulateCommandCard();
|
||||
}
|
||||
else {
|
||||
final RenderUnit rayPickUnit = this.war3MapViewer.rayPickUnit(screenX, worldScreenY,
|
||||
final RenderWidget rayPickUnit = this.war3MapViewer.rayPickUnit(screenX, worldScreenY,
|
||||
this.activeCommandUnitTargetFilter);
|
||||
final boolean shiftDown = isShiftDown();
|
||||
if (rayPickUnit != null) {
|
||||
this.unitOrderListener.issueTargetOrder(
|
||||
this.activeCommandUnit.getSimulationUnit().getHandleId(),
|
||||
this.activeCommand.getHandleId(), this.activeCommandOrderId,
|
||||
rayPickUnit.getSimulationUnit().getHandleId(), shiftDown);
|
||||
if (getSelectedUnit().soundset.yesAttack
|
||||
.playUnitResponse(this.war3MapViewer.worldScene.audioContext, getSelectedUnit())) {
|
||||
rayPickUnit.getSimulationWidget().getHandleId(), shiftDown);
|
||||
final UnitSound yesSound = (this.activeCommand instanceof CAbilityAttack)
|
||||
? getSelectedUnit().soundset.yesAttack
|
||||
: getSelectedUnit().soundset.yes;
|
||||
if (yesSound.playUnitResponse(this.war3MapViewer.worldScene.audioContext, getSelectedUnit())) {
|
||||
portraitTalk();
|
||||
}
|
||||
this.selectedSoundCount = 0;
|
||||
@ -1808,14 +1812,15 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
|
||||
else {
|
||||
if (button == Input.Buttons.RIGHT) {
|
||||
if (getSelectedUnit() != null) {
|
||||
final RenderUnit rayPickUnit = this.war3MapViewer.rayPickUnit(screenX, worldScreenY);
|
||||
if ((rayPickUnit != null) && !rayPickUnit.getSimulationUnit().isDead()) {
|
||||
final RenderWidget rayPickUnit = this.war3MapViewer.rayPickUnit(screenX, worldScreenY);
|
||||
if ((rayPickUnit != null) && !rayPickUnit.getSimulationWidget().isDead()) {
|
||||
boolean ordered = false;
|
||||
boolean rallied = false;
|
||||
for (final RenderUnit unit : this.war3MapViewer.selected) {
|
||||
boolean attacked = false;
|
||||
for (final RenderUnit unit : this.selectedUnits) {
|
||||
for (final CAbility ability : unit.getSimulationUnit().getAbilities()) {
|
||||
ability.checkCanTarget(this.war3MapViewer.simulation, unit.getSimulationUnit(),
|
||||
OrderIds.smart, rayPickUnit.getSimulationUnit(),
|
||||
OrderIds.smart, rayPickUnit.getSimulationWidget(),
|
||||
CWidgetAbilityTargetCheckReceiver.INSTANCE);
|
||||
final CWidget targetWidget = CWidgetAbilityTargetCheckReceiver.INSTANCE.getTarget();
|
||||
if (targetWidget != null) {
|
||||
@ -1823,14 +1828,17 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
|
||||
ability.getHandleId(), OrderIds.smart, targetWidget.getHandleId(),
|
||||
isShiftDown());
|
||||
rallied |= ability instanceof CAbilityRally;
|
||||
attacked |= ability instanceof CAbilityAttack;
|
||||
ordered = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (ordered) {
|
||||
if (getSelectedUnit().soundset.yesAttack.playUnitResponse(
|
||||
this.war3MapViewer.worldScene.audioContext, getSelectedUnit())) {
|
||||
final UnitSound yesSound = attacked ? getSelectedUnit().soundset.yesAttack
|
||||
: getSelectedUnit().soundset.yes;
|
||||
if (yesSound.playUnitResponse(this.war3MapViewer.worldScene.audioContext,
|
||||
getSelectedUnit())) {
|
||||
portraitTalk();
|
||||
}
|
||||
if (rallied) {
|
||||
@ -1839,53 +1847,19 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
|
||||
}
|
||||
this.selectedSoundCount = 0;
|
||||
}
|
||||
else {
|
||||
rightClickMove(screenX, worldScreenY);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.war3MapViewer.getClickLocation(clickLocationTemp, screenX, (int) worldScreenY);
|
||||
this.war3MapViewer.showConfirmation(clickLocationTemp, 0, 1, 0);
|
||||
clickLocationTemp2.set(clickLocationTemp.x, clickLocationTemp.y);
|
||||
|
||||
boolean ordered = false;
|
||||
boolean rallied = false;
|
||||
for (final RenderUnit unit : this.war3MapViewer.selected) {
|
||||
for (final CAbility ability : unit.getSimulationUnit().getAbilities()) {
|
||||
ability.checkCanUse(this.war3MapViewer.simulation, unit.getSimulationUnit(),
|
||||
OrderIds.smart, BooleanAbilityActivationReceiver.INSTANCE);
|
||||
if (BooleanAbilityActivationReceiver.INSTANCE.isOk()) {
|
||||
ability.checkCanTarget(this.war3MapViewer.simulation, unit.getSimulationUnit(),
|
||||
OrderIds.smart, clickLocationTemp2,
|
||||
PointAbilityTargetCheckReceiver.INSTANCE);
|
||||
final Vector2 target = PointAbilityTargetCheckReceiver.INSTANCE.getTarget();
|
||||
if (target != null) {
|
||||
this.unitOrderListener.issuePointOrder(
|
||||
unit.getSimulationUnit().getHandleId(), ability.getHandleId(),
|
||||
OrderIds.smart, clickLocationTemp2.x, clickLocationTemp2.y,
|
||||
isShiftDown());
|
||||
rallied |= ability instanceof CAbilityRally;
|
||||
ordered = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (ordered) {
|
||||
if (getSelectedUnit().soundset.yes.playUnitResponse(
|
||||
this.war3MapViewer.worldScene.audioContext, getSelectedUnit())) {
|
||||
portraitTalk();
|
||||
}
|
||||
if (rallied) {
|
||||
this.war3MapViewer.getUiSounds().getSound("RallyPointPlace")
|
||||
.play(this.uiScene.audioContext, 0, 0);
|
||||
}
|
||||
this.selectedSoundCount = 0;
|
||||
}
|
||||
rightClickMove(screenX, worldScreenY);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
final List<RenderUnit> selectedUnits = this.war3MapViewer.selectUnit(screenX, worldScreenY, false);
|
||||
selectUnits(selectedUnits);
|
||||
final List<RenderWidget> selectedUnits = this.war3MapViewer.selectUnit(screenX, worldScreenY,
|
||||
false);
|
||||
selectWidgets(selectedUnits);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1898,7 +1872,57 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
|
||||
return false;
|
||||
}
|
||||
|
||||
private void rightClickMove(final int screenX, final float worldScreenY) {
|
||||
this.war3MapViewer.getClickLocation(clickLocationTemp, screenX, (int) worldScreenY);
|
||||
this.war3MapViewer.showConfirmation(clickLocationTemp, 0, 1, 0);
|
||||
clickLocationTemp2.set(clickLocationTemp.x, clickLocationTemp.y);
|
||||
|
||||
boolean ordered = false;
|
||||
boolean rallied = false;
|
||||
for (final RenderUnit unit : this.selectedUnits) {
|
||||
for (final CAbility ability : unit.getSimulationUnit().getAbilities()) {
|
||||
ability.checkCanUse(this.war3MapViewer.simulation, unit.getSimulationUnit(), OrderIds.smart,
|
||||
BooleanAbilityActivationReceiver.INSTANCE);
|
||||
if (BooleanAbilityActivationReceiver.INSTANCE.isOk()) {
|
||||
ability.checkCanTarget(this.war3MapViewer.simulation, unit.getSimulationUnit(), OrderIds.smart,
|
||||
clickLocationTemp2, PointAbilityTargetCheckReceiver.INSTANCE);
|
||||
final Vector2 target = PointAbilityTargetCheckReceiver.INSTANCE.getTarget();
|
||||
if (target != null) {
|
||||
this.unitOrderListener.issuePointOrder(unit.getSimulationUnit().getHandleId(),
|
||||
ability.getHandleId(), OrderIds.smart, clickLocationTemp2.x, clickLocationTemp2.y,
|
||||
isShiftDown());
|
||||
rallied |= ability instanceof CAbilityRally;
|
||||
ordered = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (ordered) {
|
||||
if (getSelectedUnit().soundset.yes.playUnitResponse(this.war3MapViewer.worldScene.audioContext,
|
||||
getSelectedUnit())) {
|
||||
portraitTalk();
|
||||
}
|
||||
if (rallied) {
|
||||
this.war3MapViewer.getUiSounds().getSound("RallyPointPlace").play(this.uiScene.audioContext, 0, 0);
|
||||
}
|
||||
this.selectedSoundCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void selectWidgets(final List<RenderWidget> selectedUnits) {
|
||||
final List<RenderUnit> units = new ArrayList<>();
|
||||
for (final RenderWidget widget : selectedUnits) {
|
||||
if (widget instanceof RenderUnit) {
|
||||
units.add((RenderUnit) widget);
|
||||
}
|
||||
}
|
||||
selectUnits(units);
|
||||
}
|
||||
|
||||
private void selectUnits(final List<RenderUnit> selectedUnits) {
|
||||
this.selectedUnits = selectedUnits;
|
||||
if (!selectedUnits.isEmpty()) {
|
||||
final RenderUnit unit = selectedUnits.get(0);
|
||||
final boolean selectionChanged = getSelectedUnit() != unit;
|
||||
@ -2027,10 +2051,10 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
final List<RenderUnit> unitList = Arrays.asList(
|
||||
final List<RenderWidget> unitList = Arrays.asList(
|
||||
this.war3MapViewer.getRenderPeer(this.selectedUnit.getSimulationUnit().getWorkerInside()));
|
||||
this.war3MapViewer.doSelectUnit(unitList);
|
||||
selectUnits(unitList);
|
||||
selectWidgets(unitList);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user