Support for walkable destructables

This commit is contained in:
Retera 2020-10-23 22:35:25 -04:00
parent e919f35904
commit 7cea3e3fcd
17 changed files with 3237 additions and 3081 deletions

View File

@ -38,6 +38,7 @@ import com.etheller.warsmash.parsers.fdf.GameUI;
import com.etheller.warsmash.parsers.jass.Jass2.RootFrameListener;
import com.etheller.warsmash.units.DataTable;
import com.etheller.warsmash.units.Element;
import com.etheller.warsmash.units.HashedGameObject;
import com.etheller.warsmash.util.DataSourceFileHandle;
import com.etheller.warsmash.util.ImageUtils;
import com.etheller.warsmash.viewer5.CanvasProvider;
@ -141,7 +142,10 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
}
final Element cameraData = this.viewer.miscData.get("Camera");
final Element cameraListenerData = this.viewer.miscData.get("Listener");
Element cameraListenerData = this.viewer.miscData.get("Listener");
if(cameraListenerData==null) {
cameraListenerData = new Element("Listener", new DataTable(null));
}
final CameraPreset[] cameraPresets = new CameraPreset[6];
for (int i = 0; i < cameraPresets.length; i++) {
cameraPresets[i] = new CameraPreset(cameraData.getFieldFloatValue("AOA", i),

View File

@ -75,6 +75,43 @@ public class Quadtree<T> {
}
}
public boolean intersect(float x, float y, final QuadtreeIntersector<T> intersector) {
if (this.leaf) {
for (int i = 0; i < this.nodes.size; i++) {
final Node<T> node = this.nodes.get(i);
if (node.bounds.contains(x, y)) {
if (intersector.onIntersect(node.object)) {
return true;
}
}
}
return false;
}
else {
if (this.northeast.bounds.contains(x, y)) {
if (this.northeast.intersect(x, y, intersector)) {
return true;
}
}
if (this.northwest.bounds.contains(x, y)) {
if (this.northwest.intersect(x, y, intersector)) {
return true;
}
}
if (this.southwest.bounds.contains(x, y)) {
if (this.southwest.intersect(x, y, intersector)) {
return true;
}
}
if (this.southeast.bounds.contains(x, y)) {
if (this.southeast.intersect(x, y, intersector)) {
return true;
}
}
return false;
}
}
private void add(final Node<T> node, final int depth) {
if (this.leaf) {
if ((this.nodes.size >= SPLIT_THRESHOLD) && (depth < MAX_DEPTH)) {

View File

@ -31,4 +31,8 @@ public class Bounds {
public boolean intersectRayFast(final Ray ray) {
return Intersector.intersectRayBoundsFast(ray, this.boundingBox);
}
public BoundingBox getBoundingBox() {
return boundingBox;
}
}

View File

@ -35,12 +35,13 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
@Override
public InputStream read() {
return data;
};
}
;
};
if (data != null) {
return Gdx.audio.newSound(temp);
}
else {
} else {
System.err.println("Warning: missing sound file: " + this.filename);
return null;
}
@ -58,11 +59,9 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
stringBuilder.append(line);
stringBuilder.append("\n");
}
}
catch (final UnsupportedEncodingException e) {
} catch (final UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
catch (final IOException e) {
} catch (final IOException e) {
throw new RuntimeException(e);
}
return new MappedData(stringBuilder.toString());
@ -96,10 +95,10 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
/**
* If this is an SPL/UBR emitter object, ok will be set to true if the tables
* are loaded.
*
* <p>
* This is because, like the other geometry emitters, it is fine to use them
* even if the textures don't load.
*
* <p>
* The particles will simply be black.
*/
private boolean ok = false;
@ -120,11 +119,9 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
if ("SPL".equals(type)) {
this.geometryEmitterType = GeometryEmitterFuncs.EMITTER_SPLAT;
}
else if ("UBR".equals(type)) {
} else if ("UBR".equals(type)) {
this.geometryEmitterType = GeometryEmitterFuncs.EMITTER_UBERSPLAT;
}
else if ("SPN".equals(type)) {
} else if ("SPN".equals(type)) {
this.geometryEmitterType = GeometryEmitterFuncs.EMITTER_SPN;
}
@ -144,24 +141,20 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
if ("SPN".equals(type)) {
tables.add(viewer.loadGeneric(pathSolver.solve("Splats\\SpawnData.slk", solverParams).finalSrc,
FetchDataTypeName.SLK, mappedDataCallback));
}
else if ("SPL".equals(type)) {
} else if ("SPL".equals(type)) {
tables.add(viewer.loadGeneric(pathSolver.solve("Splats\\SplatData.slk", solverParams).finalSrc,
FetchDataTypeName.SLK, mappedDataCallback));
}
else if ("UBR".equals(type)) {
} else if ("UBR".equals(type)) {
tables.add(viewer.loadGeneric(pathSolver.solve("Splats\\UberSplatData.slk", solverParams).finalSrc,
FetchDataTypeName.SLK, mappedDataCallback));
}
else if ("SND".equals(type)) {
} else if ("SND".equals(type)) {
if (!model.reforged) {
tables.add(viewer.loadGeneric(pathSolver.solve("UI\\SoundInfo\\AnimLookups.slk", solverParams).finalSrc,
FetchDataTypeName.SLK, mappedDataCallback));
}
tables.add(viewer.loadGeneric(pathSolver.solve("UI\\SoundInfo\\AnimSounds.slk", solverParams).finalSrc,
FetchDataTypeName.SLK, mappedDataCallback));
}
else {
} else {
// Units\Critters\BlackStagMale\BlackStagMale.mdx has an event object named
// "Point01".
return;
@ -176,8 +169,7 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
final Float x = (Float) row.get(name);
if (x == null) {
return Float.NaN;
}
else {
} else {
return x.floatValue();
}
}
@ -190,14 +182,16 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
final Number x = (Number) row.get(name);
if (x == null) {
return defaultValue;
}
else {
} else {
return x.intValue();
}
}
private void load(final List<GenericResource> tables) {
final MappedData firstTable = (MappedData) tables.get(0).data;
if (firstTable == null) {
return;
}
final MappedDataRow row = firstTable.getRow(this.id.trim());
if (row != null) {
@ -214,8 +208,7 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
// this.internalModel.whenLoaded((model) => this.ok = model.ok)
this.ok = this.internalModel.ok;
}
}
else if ("SPL".equals(this.type) || "UBR".equals(this.type)) {
} else if ("SPL".equals(this.type) || "UBR".equals(this.type)) {
final String texturesExt = model.reforged ? ".dds" : ".blp";
this.internalTexture = (Texture) viewer.load(
@ -241,8 +234,7 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
getFloat(row, "LifespanRepeat")},
{getFloat(row, "UVDecayStart"), getFloat(row, "UVDecayEnd"),
getFloat(row, "DecayRepeat")},};
}
else {
} else {
this.columns = 1;
this.rows = 1;
this.lifeSpan = getFloat(row, "BirthTime") + getFloat(row, "PauseTime") + getFloat(row, "Decay");
@ -258,8 +250,7 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
this.blendDst = blendModes[1];
this.ok = true;
}
else if ("SND".equals(this.type)) {
} else if ("SND".equals(this.type)) {
// Only load sounds if audio is enabled.
// This is mostly to save on bandwidth and loading time, especially when loading
// full maps.
@ -288,8 +279,7 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
System.err.println("Null sound: " + fileNames[i]);
}
resources[i] = genericResource;
}
catch (final Exception exc) {
} catch (final Exception exc) {
System.err.println("Failed to load sound: " + path);
exc.printStackTrace();
}
@ -304,12 +294,10 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
this.ok = true;
}
}
}
else {
} else {
System.err.println("Unknown event object type: " + this.type + this.id);
}
}
else {
} else {
System.err.println("Unknown event object ID: " + this.type + this.id);
}
}
@ -318,13 +306,11 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
if (this.globalSequence != -1) {
return this.getValueAtTime(out, instance.counter % this.globalSequence, 0, this.globalSequence);
}
else if (instance.sequence != -1) {
} else if (instance.sequence != -1) {
final long[] interval = this.model.getSequences().get(instance.sequence).getInterval();
return this.getValueAtTime(out, instance.frame, interval[0], interval[1]);
}
else {
} else {
out[0] = this.defval[0];
return -1;
@ -338,8 +324,7 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
out[0] = 0;
return i;
}
else if (this.keyFrames[i] <= frame) {
} else if (this.keyFrames[i] <= frame) {
out[0] = 1;
return i;

View File

@ -160,14 +160,11 @@ public class MdxComplexInstance extends ModelInstance {
if ("SPN".equals(type)) {
emitter = new EventObjectSpnEmitter(this, emitterObject);
}
else if ("SPL".equals(type)) {
} else if ("SPL".equals(type)) {
emitter = new EventObjectSplEmitter(this, emitterObject);
}
else if ("UBR".equals(type)) {
} else if ("UBR".equals(type)) {
emitter = new EventObjectUbrEmitter(this, emitterObject);
}
else {
} else {
emitter = new EventObjectSndEmitter(this, emitterObject);
}
@ -234,22 +231,18 @@ public class MdxComplexInstance extends ModelInstance {
if (genericObject.parentId == -1) {
node.parent = this;
}
else {
} else {
node.parent = nodes[genericObject.parentId];
}
/// TODO: single-axis billboarding
if (genericObject.billboarded != 0) {
node.billboarded = true;
}
else if (genericObject.billboardedX != 0) {
} else if (genericObject.billboardedX != 0) {
node.billboardedX = true;
}
else if (genericObject.billboardedY != 0) {
} else if (genericObject.billboardedY != 0) {
node.billboardedY = true;
}
else if (genericObject.billboardedZ != 0) {
} else if (genericObject.billboardedZ != 0) {
node.billboardedZ = true;
}
@ -412,8 +405,7 @@ public class MdxComplexInstance extends ModelInstance {
geosetColor[3] = alphaHeap[0];
}
}
else if (forced) {
} else if (forced) {
geosetColor[0] = 1;
geosetColor[1] = 1;
geosetColor[2] = 1;
@ -464,8 +456,7 @@ public class MdxComplexInstance extends ModelInstance {
uvAnim[4] = scaleHeap[0];
}
}
else if (forced) {
} else if (forced) {
uvAnim[0] = 0;
uvAnim[1] = 0;
uvAnim[2] = 0;
@ -545,8 +536,7 @@ public class MdxComplexInstance extends ModelInstance {
this.floatingFrame = this.frame = (int) interval[0]; // TODO not cast
this.resetEventEmitters();
}
else if (this.sequenceLoopMode == SequenceLoopMode.LOOP_TO_NEXT_ANIMATION) { // faux queued animation
} else if (this.sequenceLoopMode == SequenceLoopMode.LOOP_TO_NEXT_ANIMATION) { // faux queued animation
// mode
final float framesPast = this.floatingFrame - interval[1];
@ -557,8 +547,7 @@ public class MdxComplexInstance extends ModelInstance {
this.sequenceEnded = false;
this.resetEventEmitters();
this.forced = true;
}
else {
} else {
this.floatingFrame = this.frame = (int) interval[1]; // TODO not cast
this.counter -= integerFrameTime;
this.allowParticleSpawn = false;
@ -568,8 +557,7 @@ public class MdxComplexInstance extends ModelInstance {
}
this.sequenceEnded = true;
}
else {
} else {
this.sequenceEnded = false;
}
}
@ -586,8 +574,7 @@ public class MdxComplexInstance extends ModelInstance {
// Update the batches
this.updateBatches(forced);
}
}
else {
} else {
// let variants = model.variants;
// if (forced || variants.nodes[sequenceId]) {
@ -665,8 +652,7 @@ public class MdxComplexInstance extends ModelInstance {
this.frame = 0;
this.floatingFrame = 0;
this.allowParticleSpawn = false;
}
else {
} else {
this.frame = (int) sequences.get(id).getInterval()[0]; // TODO not cast
this.floatingFrame = this.frame;
this.sequenceEnded = false;
@ -734,15 +720,17 @@ public class MdxComplexInstance extends ModelInstance {
*
* @param ray
*/
public boolean intersectRayWithCollision(final Ray ray, final Vector3 intersection, final boolean alwaysUseMesh) {
public boolean intersectRayWithCollision(final Ray ray, final Vector3 intersection, final boolean alwaysUseMesh, final boolean onlyUseMesh) {
final MdxModel mdxModel = (MdxModel) this.model;
final List<CollisionShape> collisionShapes = mdxModel.collisionShapes;
if (!onlyUseMesh) {
for (final CollisionShape collisionShape : collisionShapes) {
final MdxNode mdxNode = this.nodes[collisionShape.index];
if (collisionShape.checkIntersect(ray, mdxNode, intersection)) {
return true;
}
}
}
if (collisionShapes.isEmpty() || alwaysUseMesh) {
for (final Geoset geoset : mdxModel.geosets) {
if (!geoset.unselectable) {

View File

@ -32,14 +32,14 @@ public class MdxHandler extends ModelHandler {
Shaders.extendedShadowMap = viewer.webGL.createShaderProgram(
"#define EXTENDED_BONES\r\n" + MdxShaders.vsComplex, MdxShaders.fsComplexShadowMap);
Shaders.particles = viewer.webGL.createShaderProgram(MdxShaders.vsParticles, MdxShaders.fsParticles);
Shaders.simple = viewer.webGL.createShaderProgram(MdxShaders.vsSimple, MdxShaders.fsSimple);
//Shaders.simple = viewer.webGL.createShaderProgram(MdxShaders.vsSimple, MdxShaders.fsSimple);
// Shaders.hd = viewer.webGL.createShaderProgram(MdxShaders.vsHd, MdxShaders.fsHd);
// TODO HD reforged
// If a shader failed to compile, don't allow the handler to be registered, and
// send an error instead.
return Shaders.complex.isCompiled() && Shaders.extended.isCompiled() && Shaders.particles.isCompiled()
&& Shaders.simple.isCompiled() /* && Shaders.hd.isCompiled() */;
/* && Shaders.simple.isCompiled() && Shaders.hd.isCompiled() */;
}
@Override

View File

@ -152,6 +152,9 @@ public final class SdSequence<TYPE> {
}
private TYPE[] fixTimelineArray(final Timeline<TYPE> timeline, final TYPE[] values) {
if(values == null) {
return null;
}
if (timeline.getName().equals(AnimationMap.KLAC.getWar3id())
|| timeline.getName().equals(AnimationMap.KLBC.getWar3id())) {
final float[][] flippedColorData = new float[values.length][3];

View File

@ -2,10 +2,13 @@ package com.etheller.warsmash.viewer5.handlers.w3x;
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;
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;
@ -14,6 +17,16 @@ public class RenderDestructable extends RenderDoodad {
final float maxPitch, final float maxRoll, final float life) {
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)) {
int dotIndex = replaceableTextureFile.lastIndexOf('.');
if (dotIndex != -1) {
replaceableTextureFile = replaceableTextureFile.substring(0, dotIndex);
}
replaceableTextureFile += ".blp";
instance.setReplaceableTexture(replaceableTextureId, replaceableTextureFile);
}
}
@Override

View File

@ -13,8 +13,6 @@ import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
public class RenderDoodad {
private static final int SAMPLE_RADIUS = 4;
private static final War3ID TEX_FILE = War3ID.fromString("btxf");
private static final War3ID TEX_ID = War3ID.fromString("btxi");
public final ModelInstance instance;
private final MutableGameObject row;
private final float maxPitch;
@ -30,8 +28,7 @@ public class RenderDoodad {
if (isSimple && false) {
instance = model.addInstance(1);
}
else {
} else {
instance = model.addInstance();
((MdxComplexInstance) instance).setSequenceLoopMode(SequenceLoopMode.NEVER_LOOP);
}
@ -74,17 +71,6 @@ public class RenderDoodad {
final float defScale = row.readSLKTagFloat("defScale");
instance.uniformScale(defScale);
}
if (type == WorldEditorDataType.DESTRUCTIBLES) {
// TODO destructables need to be their own type, game simulation, etc
String replaceableTextureFile = row.getFieldAsString(TEX_FILE, 0);
final int replaceableTextureId = row.getFieldAsInteger(TEX_ID, 0);
if ((replaceableTextureFile != null) && (replaceableTextureFile.length() > 1)) {
if (!replaceableTextureFile.toLowerCase().endsWith(".blp")) {
replaceableTextureFile += ".blp";
}
instance.setReplaceableTexture(replaceableTextureId, replaceableTextureFile);
}
}
instance.setScene(map.worldScene);
this.instance = instance;

View File

@ -19,6 +19,8 @@ import java.util.function.Consumer;
import javax.imageio.ImageIO;
import com.badlogic.gdx.math.collision.BoundingBox;
import com.etheller.warsmash.util.*;
import org.apache.commons.compress.utils.IOUtils;
import org.apache.commons.compress.utils.SeekableInMemoryByteChannel;
@ -47,11 +49,6 @@ 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;
import com.etheller.warsmash.viewer5.CanvasProvider;
import com.etheller.warsmash.viewer5.GenericResource;
import com.etheller.warsmash.viewer5.Grid;
@ -118,10 +115,11 @@ public class War3MapViewer extends ModelViewer {
private static final War3ID sloc = War3ID.fromString("sloc");
private static final LoadGenericCallback stringDataCallback = new StringDataCallbackImplementation();
private static final float[] rayHeap = new float[6];
private static final Ray gdxRayHeap = new Ray();
public static final Ray gdxRayHeap = new Ray();
private static final Vector2 mousePosHeap = new Vector2();
private static final Vector3 normalHeap = new Vector3();
private static final Vector3 intersectionHeap = new Vector3();
public static final Vector3 intersectionHeap = new Vector3();
private static final Rectangle rectangleHeap = new Rectangle();
public static final StreamDataCallbackImplementation streamDataCallback = new StreamDataCallbackImplementation();
public PathSolver wc3PathSolver = PathSolver.DEFAULT;
@ -186,6 +184,11 @@ public class War3MapViewer extends ModelViewer {
private GameUI gameUI;
private Vector3 lightDirection;
private Quadtree<MdxComplexInstance> walkableObjectsTree;
private QuadtreeIntersectorFindsWalkableRenderHeight walkablesIntersector = new QuadtreeIntersectorFindsWalkableRenderHeight();
private QuadtreeIntersectorFindsHitPoint walkablesIntersectionFinder = new QuadtreeIntersectorFindsHitPoint();
private QuadtreeIntersectorFindsHighestWalkable intersectorFindsHighestWalkable = new QuadtreeIntersectorFindsHighestWalkable();
public War3MapViewer(final DataSource dataSource, final CanvasProvider canvas) {
super(dataSource, canvas);
this.gameDataSource = dataSource;
@ -311,8 +314,7 @@ public class War3MapViewer extends ModelViewer {
final LoadGenericCallback callback) {
if (this.mapMpq == null) {
return loadGeneric(path, dataType, callback);
}
else {
} else {
return loadGeneric(path, dataType, callback, this.dataSource);
}
}
@ -343,22 +345,19 @@ public class War3MapViewer extends ModelViewer {
tilesetSource = new CompoundDataSource(Arrays.asList(compoundDataSource,
new SubdirDataSource(compoundDataSource, tileset + ".mpq/"),
new SubdirDataSource(compoundDataSource, "_tilesets/" + tileset + ".w3mod/")));
}
else {
} else {
final byte[] mapData = IOUtils.toByteArray(mapStream);
sbc = new SeekableInMemoryByteChannel(mapData);
final DataSource internalMpqContentsDataSource = new MpqDataSource(new MPQArchive(sbc), sbc);
tilesetSource = new CompoundDataSource(
Arrays.asList(compoundDataSource, internalMpqContentsDataSource));
}
}
catch (final IOException exc) {
} catch (final IOException exc) {
tilesetSource = new CompoundDataSource(
Arrays.asList(compoundDataSource, new SubdirDataSource(compoundDataSource, tileset + ".mpq/"),
new SubdirDataSource(compoundDataSource, "_tilesets/" + tileset + ".w3mod/")));
}
}
catch (final MPQException e) {
} catch (final MPQException e) {
throw new RuntimeException(e);
}
setDataSource(tilesetSource);
@ -432,8 +431,7 @@ public class War3MapViewer extends ModelViewer {
modelInstance.setScene(War3MapViewer.this.worldScene);
if (bounceIndex == 0) {
SequenceUtils.randomBirthSequence(modelInstance);
}
else {
} else {
SequenceUtils.randomStandSequence(modelInstance);
}
modelInstance.setLocation(x, y, height);
@ -503,13 +501,13 @@ public class War3MapViewer extends ModelViewer {
War3MapViewer.this.worldScene.removeInstance(renderUnit.instance);
}
}, this.terrain.pathingGrid,
new Rectangle(centerOffset[0], centerOffset[1], (mapSize[0] * 128f) - 128, (mapSize[1] * 128f) - 128),
terrain.getEntireMap(),
this.seededRandom, w3iFile.getPlayers());
walkableObjectsTree = new Quadtree<>(terrain.getEntireMap());
if (this.doodadsAndDestructiblesLoaded) {
this.loadDoodadsAndDestructibles(this.allObjectData);
}
else {
} else {
throw new IllegalStateException("transcription of JS has not loaded a map and has no JS async promises");
}
@ -530,8 +528,7 @@ public class War3MapViewer extends ModelViewer {
public void loadAfterUI() throws IOException {
if (this.unitsAndItemsLoaded) {
this.loadUnitsAndItems(this.allObjectData);
}
else {
} else {
throw new IllegalStateException("transcription of JS has not loaded a map and has no JS async promises");
}
@ -601,8 +598,7 @@ public class War3MapViewer extends ModelViewer {
bufferedImage = TgaFile.readTGA(pathingTexture,
this.mapMpq.getResourceAsStream(pathingTexture));
this.filePathToPathingMap.put(pathingTexture.toLowerCase(), bufferedImage);
}
catch (final Exception exc) {
} catch (final Exception exc) {
exc.printStackTrace();
}
}
@ -620,23 +616,30 @@ public class War3MapViewer extends ModelViewer {
String path;
if (this.mapMpq.has(fileVar)) {
path = fileVar;
}
else {
} else {
path = file;
}
MdxModel model;
if (this.mapMpq.has(path)) {
model = (MdxModel) this.load(path, this.mapPathSolver, this.solverParams);
}
else {
} else {
model = (MdxModel) this.load(fileVar, this.mapPathSolver, this.solverParams);
}
if (type == WorldEditorDataType.DESTRUCTIBLES) {
this.doodads.add(new RenderDestructable(this, model, row, doodad, type, maxPitch, maxRoll,
doodad.getLife()));
RenderDestructable renderDestructable = new RenderDestructable(this, model, row, doodad, type, maxPitch, maxRoll,
doodad.getLife());
if (row.readSLKTagBoolean("walkable")) {
float x = doodad.getLocation()[0];
float y = doodad.getLocation()[1];
BoundingBox boundingBox = model.bounds.getBoundingBox();
float minX = boundingBox.min.x + x;
float minY = boundingBox.min.y + y;
Rectangle renderDestructableBounds = new Rectangle(minX, minY, boundingBox.getWidth(), boundingBox.getHeight());
walkableObjectsTree.add((MdxComplexInstance) renderDestructable.instance, renderDestructableBounds);
}
else {
this.doodads.add(renderDestructable);
} else {
this.doodads.add(new RenderDoodad(this, model, row, doodad, type, maxPitch, maxRoll));
}
}
@ -669,14 +672,12 @@ public class War3MapViewer extends ModelViewer {
pathingTextureImage = TgaFile.readTGA(pathingTexture,
this.mapMpq.getResourceAsStream(pathingTexture));
this.filePathToPathingMap.put(pathingTexture.toLowerCase(), pathingTextureImage);
}
catch (final Exception exc) {
} catch (final Exception exc) {
exc.printStackTrace();
}
}
}
}
else {
} else {
pathingTextureImage = null;
}
if (pathingTextureImage != null) {
@ -734,8 +735,7 @@ public class War3MapViewer extends ModelViewer {
// path = "Objects\\StartLocation\\StartLocation.mdx";
type = null; /// ??????
this.startLocations[unit.getPlayer()] = new Vector2(unit.getLocation()[0], unit.getLocation()[1]);
}
else {
} else {
row = modifications.getUnits().get(unit.getId());
if (row == null) {
row = modifications.getItems().get(unit.getId());
@ -768,8 +768,7 @@ public class War3MapViewer extends ModelViewer {
path += ".mdx";
}
}
else {
} else {
type = WorldEditorDataType.UNITS;
path = row.getFieldAsString(UNIT_FILE, 0);
@ -831,8 +830,7 @@ public class War3MapViewer extends ModelViewer {
if (pathingTexture.toLowerCase().endsWith(".tga")) {
buildingPathingPixelMap = TgaFile.readTGA(pathingTexture,
this.mapMpq.getResourceAsStream(pathingTexture));
}
else {
} else {
try (InputStream stream = this.mapMpq.getResourceAsStream(pathingTexture)) {
buildingPathingPixelMap = ImageIO.read(stream);
System.out.println("LOADING BLP PATHING: " + pathingTexture);
@ -861,8 +859,7 @@ public class War3MapViewer extends ModelViewer {
final String portraitPath = path.substring(0, path.length() - 4) + "_portrait.mdx";
if (this.dataSource.has(portraitPath)) {
portraitModel = (MdxModel) this.load(portraitPath, this.mapPathSolver, this.solverParams);
}
else {
} else {
portraitModel = model;
}
if (type == WorldEditorDataType.UNITS) {
@ -882,8 +879,7 @@ public class War3MapViewer extends ModelViewer {
}
});
}
}
else {
} else {
this.items.add(new RenderItem(this, model, row, unit, soundset, portraitModel)); // TODO store
// somewhere
if (unitShadowSplat != null) {
@ -895,8 +891,7 @@ public class War3MapViewer extends ModelViewer {
});
}
}
}
else {
} else {
System.err.println("Unknown unit ID: " + unit.getId());
}
}
@ -1081,6 +1076,13 @@ public class War3MapViewer extends ModelViewer {
gdxRayHeap.direction.nor();// needed for libgdx
RenderMathUtils.intersectRayTriangles(gdxRayHeap, this.terrain.softwareGroundMesh.vertices,
this.terrain.softwareGroundMesh.indices, 3, out);
rectangleHeap.set(Math.min(out.x, gdxRayHeap.origin.x), Math.min(out.y, gdxRayHeap.origin.y), Math.abs(out.x - gdxRayHeap.origin.x), Math.abs(out.y - gdxRayHeap.origin.y));
walkableObjectsTree.intersect(rectangleHeap, walkablesIntersectionFinder.reset(gdxRayHeap));
if (walkablesIntersectionFinder.found) {
out.set(walkablesIntersectionFinder.intersection);
} else {
out.z = Math.max(getWalkableRenderHeight(out.x, out.y), terrain.getGroundHeight(out.x, out.y));
}
}
public void showConfirmation(final Vector3 position, final float red, final float green, final float blue) {
@ -1106,7 +1108,7 @@ public class War3MapViewer extends ModelViewer {
final MdxComplexInstance instance = unit.instance;
if (instance.isVisible(this.worldScene.camera)
&& instance.intersectRayWithCollision(gdxRayHeap, intersectionHeap,
unit.getSimulationUnit().getUnitType().isBuilding())
unit.getSimulationUnit().getUnitType().isBuilding(), false)
&& !unit.getSimulationUnit().isDead()) {
entity = unit;
}
@ -1118,16 +1120,13 @@ public class War3MapViewer extends ModelViewer {
final int idx = sel.indexOf(entity);
if (idx >= 0) {
sel.remove(idx);
}
else {
} else {
sel.add(entity);
}
}
else {
} else {
sel = Arrays.asList(entity);
}
}
else {
} else {
sel = Collections.emptyList();
}
this.doSelectUnit(sel);
@ -1145,7 +1144,7 @@ public class War3MapViewer extends ModelViewer {
for (final RenderUnit unit : this.units) {
final MdxComplexInstance instance = unit.instance;
if (instance.isVisible(this.worldScene.camera) && instance.intersectRayWithCollision(gdxRayHeap,
intersectionHeap, unit.getSimulationUnit().getUnitType().isBuilding())) {
intersectionHeap, unit.getSimulationUnit().getUnitType().isBuilding(), false)) {
entity = unit;
}
}
@ -1162,11 +1161,9 @@ public class War3MapViewer extends ModelViewer {
stringBuilder.append(line);
stringBuilder.append("\n");
}
}
catch (final UnsupportedEncodingException e) {
} catch (final UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
catch (final IOException e) {
} catch (final IOException e) {
throw new RuntimeException(e);
}
return new MappedData(stringBuilder.toString());
@ -1186,11 +1183,9 @@ public class War3MapViewer extends ModelViewer {
stringBuilder.append(line);
stringBuilder.append("\n");
}
}
catch (final UnsupportedEncodingException e) {
} catch (final UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
catch (final IOException e) {
} catch (final IOException e) {
throw new RuntimeException(e);
}
return stringBuilder.toString();
@ -1252,16 +1247,13 @@ public class War3MapViewer extends ModelViewer {
ability.onOrder(this.simulation, unit.getSimulationUnit(), OrderIds.smart, mousePosHeap,
false);
ordered = true;
}
else {
} else {
System.err.println("Target not valid.");
}
}
else {
} else {
System.err.println("Ability not ok to use.");
}
}
else {
} else {
System.err.println("Ability not move.");
}
}
@ -1285,16 +1277,13 @@ public class War3MapViewer extends ModelViewer {
ability.onOrder(this.simulation, unit.getSimulationUnit(), OrderIds.smart, targetWidget,
false);
ordered = true;
}
else {
} else {
System.err.println("Target not valid.");
}
}
else {
} else {
System.err.println("Ability not ok to use.");
}
}
else {
} else {
System.err.println("Ability not move.");
}
}
@ -1354,8 +1343,7 @@ public class War3MapViewer extends ModelViewer {
public SceneLightManager createLightManager(final boolean simple) {
if (simple) {
return new W3xScenePortraitLightManager(this, this.lightDirection);
}
else {
} else {
return new W3xSceneWorldLightManager(this);
}
}
@ -1376,4 +1364,80 @@ public class War3MapViewer extends ModelViewer {
public AbilityDataUI getAbilityDataUI() {
return this.abilityDataUI;
}
public float getWalkableRenderHeight(float x, float y) {
walkableObjectsTree.intersect(x, y, walkablesIntersector.reset(x, y));
return walkablesIntersector.z;
}
public MdxComplexInstance getHighestWalkableUnder(float x, float y) {
walkableObjectsTree.intersect(x, y, intersectorFindsHighestWalkable.reset(x, y));
return intersectorFindsHighestWalkable.highestInstance;
}
private static final class QuadtreeIntersectorFindsWalkableRenderHeight implements QuadtreeIntersector<MdxComplexInstance> {
private float z;
private Ray ray = new Ray();
private Vector3 intersection = new Vector3();
private QuadtreeIntersectorFindsWalkableRenderHeight reset(float x, float y) {
z = -Float.MAX_VALUE;
ray.set(x, y, 4096, 0, 0, -8192);
return this;
}
@Override
public boolean onIntersect(MdxComplexInstance intersectingObject) {
if (intersectingObject.intersectRayWithCollision(ray, intersection, true, true)) {
z = Math.max(z, intersection.z);
}
return false;
}
}
private static final class QuadtreeIntersectorFindsHighestWalkable implements QuadtreeIntersector<MdxComplexInstance> {
private float z;
private Ray ray = new Ray();
private Vector3 intersection = new Vector3();
private MdxComplexInstance highestInstance;
private QuadtreeIntersectorFindsHighestWalkable reset(float x, float y) {
z = -Float.MAX_VALUE;
ray.set(x, y, 4096, 0, 0, -8192);
highestInstance = null;
return this;
}
@Override
public boolean onIntersect(MdxComplexInstance intersectingObject) {
if (intersectingObject.intersectRayWithCollision(ray, intersection, true, true)) {
if(intersection.z > z) {
z = intersection.z;
highestInstance = intersectingObject;
}
}
return false;
}
}
private static final class QuadtreeIntersectorFindsHitPoint implements QuadtreeIntersector<MdxComplexInstance> {
private Ray ray;
private Vector3 intersection = new Vector3();
private boolean found;
private QuadtreeIntersectorFindsHitPoint reset(Ray ray) {
this.ray = ray;
this.found = false;
return this;
}
@Override
public boolean onIntersect(MdxComplexInstance intersectingObject) {
if (intersectingObject.intersectRayWithCollision(ray, intersection, true, true)) {
this.found = true;
return true;
}
return false;
}
}
}

View File

@ -411,6 +411,7 @@ public class Terrain {
this.shaderMapBoundsRectangle = new Rectangle(this.shaderMapBounds[0], this.shaderMapBounds[1],
this.shaderMapBounds[2] - this.shaderMapBounds[0], this.shaderMapBounds[3] - this.shaderMapBounds[1]);
this.mapSize = w3eFile.getMapSize();
this.entireMapRectangle = new Rectangle(centerOffset[0], centerOffset[1], (mapSize[0] * 128f) - 128, (mapSize[1] * 128f) - 128);
this.softwareGroundMesh = new SoftwareGroundMesh(this.groundHeights, this.groundCornerHeights,
this.centerOffset, width, height);
@ -1019,15 +1020,14 @@ public class Terrain {
gl.glUniform1i(6, (int) this.waterIndex);
gl.glUniform1f(this.waterShader.getUniformLocation("centerOffsetX"), this.centerOffset[0]);
gl.glUniform1f(this.waterShader.getUniformLocation("centerOffsetY"), this.centerOffset[1]);
gl.glUniform4fv(9, 1, this.shaderMapBounds, 0);
gl.glUniform4fv(11, 1, this.shaderMapBounds, 0);
final W3xSceneLightManager lightManager = (W3xSceneLightManager) this.viewer.worldScene.getLightManager();
final DataTexture unitLightsTexture = lightManager.getTerrainLightsTexture();
final DataTexture terrainLightsTexture = lightManager.getTerrainLightsTexture();
unitLightsTexture.bind(21);
gl.glUniform1i(this.waterShader.getUniformLocation("lightTexture"), 21);
gl.glUniform1f(this.waterShader.getUniformLocation("lightCount"), lightManager.getTerrainLightCount());
gl.glUniform1f(this.waterShader.getUniformLocation("lightCountHeight"), unitLightsTexture.getHeight());
terrainLightsTexture.bind(3);
gl.glUniform1f(9, lightManager.getTerrainLightCount());
gl.glUniform1f(10, terrainLightsTexture.getHeight());
gl.glActiveTexture(GL30.GL_TEXTURE0);
gl.glBindTexture(GL30.GL_TEXTURE_2D, this.waterHeight);
@ -1035,7 +1035,7 @@ public class Terrain {
gl.glBindTexture(GL30.GL_TEXTURE_2D, this.groundCornerHeight);
gl.glActiveTexture(GL30.GL_TEXTURE2);
gl.glBindTexture(GL30.GL_TEXTURE_2D, this.waterExists);
gl.glActiveTexture(GL30.GL_TEXTURE3);
gl.glActiveTexture(GL30.GL_TEXTURE4);
gl.glBindTexture(GL30.GL_TEXTURE_2D_ARRAY, this.waterTextureArray);
gl.glBindBuffer(GL30.GL_ARRAY_BUFFER, Shapes.INSTANCE.vertexBuffer);
@ -1239,6 +1239,7 @@ public class Terrain {
private final WaveBuilder waveBuilder;
public PathingGrid pathingGrid;
private final Rectangle shaderMapBoundsRectangle;
private final Rectangle entireMapRectangle;
private static final class UnloadedTexture {
private final int width;
@ -1399,4 +1400,8 @@ public class Terrain {
public Rectangle getPlayableMapArea() {
return this.shaderMapBoundsRectangle;
}
public Rectangle getEntireMap() {
return entireMapRectangle;
}
}

View File

@ -428,9 +428,9 @@ public class TerrainShaders {
"layout (location = 3) uniform vec4 deep_color_min;\r\n" + //
"layout (location = 4) uniform vec4 deep_color_max;\r\n" + //
"layout (location = 5) uniform float water_offset;\r\n" + //
"layout (location = 10) uniform sampler2D lightTexture;\r\n" + //
"layout (location = 11) uniform float lightCount;\r\n" + //
"layout (location = 12) uniform float lightTextureHeight;\r\n" + //
"layout (binding = 3) uniform sampler2D lightTexture;\r\n" + //
"layout (location = 9) uniform float lightCount;\r\n" + //
"layout (location = 10) uniform float lightTextureHeight;\r\n" + //
"\r\n" + //
"out vec2 UV;\r\n" + //
"out vec4 Color;\r\n" + //
@ -477,12 +477,12 @@ public class TerrainShaders {
public static final String frag = "#version 450 core\r\n" + //
"\r\n" + //
"layout (binding = 3) uniform sampler2DArray water_textures;\r\n" + //
"layout (binding = 4) uniform sampler2DArray water_textures;\r\n" + //
"layout (binding = 2) uniform sampler2D water_exists_texture;\r\n" + //
"\r\n" + //
"\r\n" + //
"layout (location = 6) uniform int current_texture;\r\n" + //
"layout (location = 9) uniform vec4 mapBounds;\r\n" + //
"layout (location = 11) uniform vec4 mapBounds;\r\n" + //
"\r\n" + //
"in vec2 UV;\r\n" + //
"in vec4 Color;\r\n" + //

View File

@ -89,7 +89,8 @@ public class RenderUnit {
this.unitAnimationListenerImpl = new UnitAnimationListenerImpl(instance);
simulationUnit.setUnitAnimationListener(this.unitAnimationListenerImpl);
final String requiredAnimationNames = row.getFieldAsString(ANIM_PROPS, 0);
TokenLoop: for (final String animationName : requiredAnimationNames.split(",")) {
TokenLoop:
for (final String animationName : requiredAnimationNames.split(",")) {
final String upperCaseToken = animationName.toUpperCase();
for (final SecondaryTag secondaryTag : SecondaryTag.values()) {
if (upperCaseToken.equals(secondaryTag.name())) {
@ -152,8 +153,7 @@ public class RenderUnit {
// where they actually are
this.x += (speedDelta * simDx) / distanceToSimulation;
this.y += (speedDelta * simDy) / distanceToSimulation;
}
else {
} else {
this.x = simulationX;
this.y = simulationY;
}
@ -166,20 +166,42 @@ public class RenderUnit {
final float groundHeight;
final MovementType movementType = this.simulationUnit.getUnitType().getMovementType();
final short terrainPathing = map.terrain.pathingGrid.getPathing(x, y);
final boolean swimming = (movementType == MovementType.AMPHIBIOUS)
boolean swimming = (movementType == MovementType.AMPHIBIOUS)
&& PathingGrid.isPathingFlag(terrainPathing, PathingGrid.PathingType.SWIMMABLE)
&& !PathingGrid.isPathingFlag(terrainPathing, PathingGrid.PathingType.WALKABLE);
if ((swimming) || (movementType == MovementType.FLOAT) || (movementType == MovementType.FLY)
|| (movementType == MovementType.HOVER)) {
groundHeight = Math.max(map.terrain.getGroundHeight(x, y), map.terrain.getWaterHeight(x, y));
float groundHeightTerrain = map.terrain.getGroundHeight(x, y);
float groundHeightTerrainAndWater;
MdxComplexInstance currentWalkableUnder;
boolean standingOnWater = (swimming) || (movementType == MovementType.FLOAT) || (movementType == MovementType.FLY)
|| (movementType == MovementType.HOVER);
if (standingOnWater) {
groundHeightTerrainAndWater = Math.max(groundHeightTerrain, map.terrain.getWaterHeight(x, y));
} else {
// land units will have their feet pass under the surface of the water
groundHeightTerrainAndWater = groundHeightTerrain;
}
if(movementType == MovementType.FLOAT) {
// boats cant go on bridges
groundHeight = groundHeightTerrainAndWater;
currentWalkableUnder = null;
} else {
currentWalkableUnder = map.getHighestWalkableUnder(x, y);
if(currentWalkableUnder != null) {
System.out.println("WALKABLE UNDER");
}
War3MapViewer.gdxRayHeap.set(x, y, 4096, 0, 0, -8192);
if (currentWalkableUnder != null && currentWalkableUnder.intersectRayWithCollision(War3MapViewer.gdxRayHeap, War3MapViewer.intersectionHeap, true, true)
&& War3MapViewer.intersectionHeap.z > groundHeightTerrainAndWater) {
groundHeight = War3MapViewer.intersectionHeap.z;
swimming = false; // Naga Royal Guard should slither across a bridge, not swim in rock
} else {
groundHeight = groundHeightTerrainAndWater;
currentWalkableUnder = null;
}
else {
groundHeight = map.terrain.getGroundHeight(x, y);
}
if (swimming && !this.swimming) {
this.unitAnimationListenerImpl.addSecondaryTag(AnimationTokens.SecondaryTag.SWIM);
}
else if (!swimming && this.swimming) {
} else if (!swimming && this.swimming) {
this.unitAnimationListenerImpl.removeSecondaryTag(AnimationTokens.SecondaryTag.SWIM);
}
this.swimming = swimming;
@ -200,8 +222,7 @@ public class RenderUnit {
if (boneCorpse && !this.boneCorpse) {
this.unitAnimationListenerImpl.playAnimationWithDuration(true, PrimaryTag.DECAY, SequenceUtils.BONE,
this.simulationUnit.getEndingDecayTime(map.simulation), true);
}
else if (corpse && !this.corpse) {
} else if (corpse && !this.corpse) {
this.unitAnimationListenerImpl.playAnimationWithDuration(true, PrimaryTag.DECAY, SequenceUtils.FLESH,
map.simulation.getGameplayConstants().getDecayTime(), true);
}
@ -237,8 +258,7 @@ public class RenderUnit {
- ((this.orientationInterpolation.getEndingAccelCutoff() - absoluteFacingDeltaRadians)
/ this.orientationInterpolation.getEndingAccelCutoff()))
* (this.orientationInterpolation.getMaxVelocity()) * turningSign;
}
else {
} else {
acceleration = this.orientationInterpolation.getStartingAcceleration() * turningSign;
this.currentTurnVelocity = this.currentTurnVelocity + acceleration;
}
@ -263,17 +283,43 @@ public class RenderUnit {
final float pitchSampleForwardY = y + (sampleRadius * (float) Math.sin(facingRadians));
final float pitchSampleBackwardX = x - (sampleRadius * (float) Math.cos(facingRadians));
final float pitchSampleBackwardY = y - (sampleRadius * (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, sampleRadius * 2)));
final double leftOfFacingAngle = facingRadians + (Math.PI / 2);
final float rollSampleForwardX = x + (sampleRadius * (float) Math.cos(leftOfFacingAngle));
final float rollSampleForwardY = y + (sampleRadius * (float) Math.sin(leftOfFacingAngle));
final float rollSampleBackwardX = x - (sampleRadius * (float) Math.cos(leftOfFacingAngle));
final float rollSampleBackwardY = y - (sampleRadius * (float) Math.sin(leftOfFacingAngle));
final float rollSampleGroundHeight1 = map.terrain.getGroundHeight(rollSampleBackwardX, rollSampleBackwardY);
final float rollSampleGroundHeight2 = map.terrain.getGroundHeight(rollSampleForwardX, rollSampleForwardY);
final float pitchSampleGroundHeight1;
final float pitchSampleGroundHeight2;
final float rollSampleGroundHeight1;
final float rollSampleGroundHeight2;
if (currentWalkableUnder != null) {
pitchSampleGroundHeight1 = getGroundHeightSample(groundHeight, currentWalkableUnder, pitchSampleBackwardX, pitchSampleBackwardY);
pitchSampleGroundHeight2 = getGroundHeightSample(groundHeight, currentWalkableUnder, pitchSampleForwardX, pitchSampleForwardY);
rollSampleGroundHeight1 = getGroundHeightSample(groundHeight, currentWalkableUnder, rollSampleBackwardX, rollSampleBackwardY);
rollSampleGroundHeight2 = getGroundHeightSample(groundHeight, currentWalkableUnder, rollSampleForwardX, rollSampleForwardY);
} else {
float pitchGroundHeight1 = map.terrain.getGroundHeight(pitchSampleBackwardX, pitchSampleBackwardY);
float pitchGroundHeight2 = map.terrain.getGroundHeight(pitchSampleForwardX, pitchSampleForwardY);
float rollGroundHeight1 = map.terrain.getGroundHeight(rollSampleBackwardX, rollSampleBackwardY);
float rollGroundHeight2 = map.terrain.getGroundHeight(rollSampleForwardX, rollSampleForwardY);
if (standingOnWater) {
pitchSampleGroundHeight1 = Math.max(pitchGroundHeight1,
map.terrain.getWaterHeight(pitchSampleBackwardX, pitchSampleBackwardY));
pitchSampleGroundHeight2 = Math.max(pitchGroundHeight2,
map.terrain.getWaterHeight(pitchSampleForwardX, pitchSampleForwardY));
rollSampleGroundHeight1 = Math.max(rollGroundHeight1,
map.terrain.getWaterHeight(rollSampleBackwardX, rollSampleBackwardY));
rollSampleGroundHeight2 = Math.max(rollGroundHeight2,
map.terrain.getWaterHeight(rollSampleForwardX, rollSampleForwardY));
} else {
pitchSampleGroundHeight1 = pitchGroundHeight1;
pitchSampleGroundHeight2 = pitchGroundHeight2;
rollSampleGroundHeight1 = rollGroundHeight1;
rollSampleGroundHeight2 = rollGroundHeight2;
}
}
pitch = Math.max(-maxPitch, Math.min(maxPitch,
(float) Math.atan2(pitchSampleGroundHeight2 - pitchSampleGroundHeight1, sampleRadius * 2)));
roll = Math.max(-maxRoll, Math.min(maxRoll,
(float) Math.atan2(rollSampleGroundHeight2 - rollSampleGroundHeight1, sampleRadius * 2)));
this.instance.rotate(tempQuat.setFromAxisRad(RenderMathUtils.VEC3_UNIT_Y, -pitch));
@ -289,6 +335,18 @@ public class RenderUnit {
this.unitAnimationListenerImpl.update();
}
private float getGroundHeightSample(float groundHeight, MdxComplexInstance currentWalkableUnder, float sampleX, float sampleY) {
final float sampleGroundHeight;
War3MapViewer.gdxRayHeap.origin.x = sampleX;
War3MapViewer.gdxRayHeap.origin.y = sampleY;
if (currentWalkableUnder.intersectRayWithCollision(War3MapViewer.gdxRayHeap, War3MapViewer.intersectionHeap, true, true)) {
sampleGroundHeight = War3MapViewer.intersectionHeap.z;
} else {
sampleGroundHeight = groundHeight;
}
return sampleGroundHeight;
}
public CUnit getSimulationUnit() {
return this.simulationUnit;
}
@ -374,8 +432,7 @@ public class RenderUnit {
// animation is a looping animation
playAnimation(true, this.currentAnimation, this.currentAnimationSecondaryTags,
this.currentSpeedRatio, this.currentlyAllowingRarityVariations);
}
else {
} else {
final QueuedAnimation nextAnimation = this.animationQueue.poll();
if (nextAnimation != null) {
playAnimation(true, nextAnimation.animationName, nextAnimation.secondaryAnimationTags, 1.0f,

View File

@ -178,6 +178,7 @@ public class CUnitData {
final List<CUnitAttack> attacks = new ArrayList<>();
final int attacksEnabled = unitType.getFieldAsInteger(ATTACKS_ENABLED, 0);
if ((attacksEnabled & 0x1) != 0) {
try {
// attack one
final float animationBackswingPoint = unitType.getFieldAsFloat(ATTACK1_BACKSWING_POINT, 0);
final float animationDamagePoint = unitType.getFieldAsFloat(ATTACK1_DAMAGE_POINT, 0);
@ -218,8 +219,12 @@ public class CUnitData {
damageSidesPerDie, damageSpillDistance, damageSpillRadius, damageUpgradeAmount,
maximumNumberOfTargets, projectileArc, projectileArt, projectileHomingEnabled, projectileSpeed,
range, rangeMotionBuffer, showUI, targetsAllowed, weaponSound, weaponType));
} catch (Exception exc) {
System.err.println("Attack 1 failed to parse with: " + exc.getClass() + ":" + exc.getMessage());
}
}
if ((attacksEnabled & 0x2) != 0) {
try {
// attack two
final float animationBackswingPoint = unitType.getFieldAsFloat(ATTACK2_BACKSWING_POINT, 0);
final float animationDamagePoint = unitType.getFieldAsFloat(ATTACK2_DAMAGE_POINT, 0);
@ -260,6 +265,9 @@ public class CUnitData {
damageSidesPerDie, damageSpillDistance, damageSpillRadius, damageUpgradeAmount,
maximumNumberOfTargets, projectileArc, projectileArt, projectileHomingEnabled, projectileSpeed,
range, rangeMotionBuffer, showUI, targetsAllowed, weaponSound, weaponType));
} catch (Exception exc) {
System.err.println("Attack 2 failed to parse with: " + exc.getClass() + ":" + exc.getMessage());
}
}
final int deathType = unitType.getFieldAsInteger(DEATH_TYPE, 0);
final boolean raise = (deathType & 0x1) != 0;

View File

@ -138,7 +138,6 @@ public class CAttackOrder implements COrder {
else {
damage = simulation.getSeededRandom().nextInt(maxDamage - minDamage) + minDamage;
}
System.out.println(damage + " from " + minDamage + " to " + maxDamage);
this.unitAttack.launch(simulation, this.unit, this.target, damage);
this.damagePointLaunchTime = 0;
}

View File

@ -177,7 +177,8 @@ public class CPathfindingProcessor {
}
}
while (!openSet.isEmpty()) {
int searchIterations = 0;
while (!openSet.isEmpty() && searchIterations < 150000) {
Node current = openSet.poll();
if (isGoal(current)) {
final LinkedList<Point2D.Float> totalPath = new LinkedList<>();
@ -259,6 +260,7 @@ public class CPathfindingProcessor {
}
}
}
searchIterations++;
}
return Collections.emptyList();
}

View File

@ -24,7 +24,7 @@ import com.etheller.warsmash.viewer5.gl.SoundLengthExtension;
import com.etheller.warsmash.viewer5.gl.WireframeExtension;
public class DesktopLauncher {
public static void main(final String[] arg) {
public static void main(String[] arg) {
Extensions.angleInstancedArrays = new ANGLEInstancedArrays() {
@Override
public void glVertexAttribDivisorANGLE(final int index, final int divisor) {
@ -83,10 +83,11 @@ public class DesktopLauncher {
config.useGL30 = true;
config.gles30ContextMajorVersion = 3;
config.gles30ContextMinorVersion = 3;
config.samples = 16;
//config.samples = 16;
// config.vSyncEnabled = false;
// config.foregroundFPS = 0;
// config.backgroundFPS = 0;
arg = new String[]{"-windowed"};
if ((arg.length > 0) && "-windowed".equals(arg[0])) {
config.fullscreen = false;
}